Which String Type to Use?
Decision guide for choosing between std::string, std::string_view, const char*, std::string_view literals, and owning vs non-owning string types in C++.
Quick Decision
cpp
Do you need to OWN the string data (store it, modify it)?
├── YES → std::string
│ ├── Concatenating / modifying? → std::string
│ └── Building from many pieces? → std::string + reserve()
│ or std::ostringstream / std::format
└── NO (just reading/passing?)
├── C++17+?
│ └── YES → std::string_view (always prefer over const std::string&)
└── C++14-?
└── const std::string& (or const char* for C interop)
Is this a function parameter?
├── Takes ownership / stores it → std::string (by value)
├── Read-only, any string type → std::string_view
└── Output / in-out param → std::string& (reference)
Is this a compile-time constant?
└── YES → constexpr std::string_view or constexpr std::string (C++20)std::string — Owning, Mutable
Use when you need to store, build, or modify a string.
cpp
// Storage member — must own
struct Config {
std::string host; // owns the data
std::string path;
};
// Building strings
std::string result;
result.reserve(256);
for (const auto& part : parts)
result += part;
// Modification
std::string name = "Hello World";
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
// Return from function
std::string format_id(int n) {
return std::format("ID-{:05d}", n); // move on return
}Cost: heap allocation (unless SSO kicks in for short strings, typically ≤15 chars).
std::string_view — Non-Owning, Read-Only
Use for parameters and locals that just observe a string. Zero allocation.
cpp
// Function parameter — accepts string, string_view, const char*, literals
bool starts_with_http(std::string_view url) {
return url.starts_with("http");
}
starts_with_http("https://example.com"); // no allocation
starts_with_http(my_string); // no allocation
starts_with_http(some_string_view); // no allocation
// Local view into existing data
std::string big = load_file("data.txt");
std::string_view first_line{big.data(),
big.find('\n') == std::string::npos ? big.size() : big.find('\n')};
// Safe: view into string literal (static lifetime)
constexpr std::string_view greeting = "Hello";Warning: dangling view — do not store a string_view into a temporary std::string:
cpp
std::string_view danger() {
std::string s = "temp";
return s; // UB: s destroyed, view dangles
}const char* — C Interop
cpp
// C API calls
FILE* f = fopen(path.c_str(), "r"); // need c_str() from std::string
sqlite3_exec(db, sql.c_str(), ...);
// String literals — compile-time constant
const char* label = "Version"; // points to static storage
// Avoid in new C++ code — no size, no RAII, easy to misusestd::string_view Literals (C++14)
cpp
using namespace std::string_view_literals;
constexpr auto sv = "hello"sv; // std::string_view, no allocation
constexpr auto str = "hello"s; // std::string (allocates at runtime)
// Useful in switch/map keys
static const std::unordered_map<std::string_view, int> codes{
{"OK"sv, 200}, {"Not Found"sv, 404}, {"Error"sv, 500}
};Function Parameter Summary
| What you want | Parameter type |
|---|---|
| Read-only, any source | std::string_view |
| Store/own (sink) | std::string by value |
| Modify in-place | std::string& |
| C API interop | const char* |
| Compile-time only | std::string_view (constexpr) |
cpp
// Read-only — best: string_view
void log(std::string_view msg);
// Sink — best: by value, then move
void set_name(std::string name) { name_ = std::move(name); }
// Avoid: const string& (forces std::string if passed a literal or string_view)
void old_style(const std::string& s); // suboptimal for literals
// Avoid: const char* (doesn't work for std::string or string_view without .c_str())
void c_style(const char* s); // callers must remember .c_str()Comparison Table
std::string | std::string_view | const char* | |
|---|---|---|---|
| Owns data | Yes | No | No |
| Mutable | Yes | No | No* |
| Size known | Yes | Yes | No (strlen) |
| Null-terminated | Yes | No (may not be) | Yes |
| Heap allocation | Yes (usually) | Never | Never |
| SSO (short string opt) | Yes | N/A | N/A |
| Range-for | Yes | Yes | No |
std::format | Yes | Yes | Yes |
C API (needs \0) | .c_str() | Not safe (no \0) | Yes |
| C++17 requirement | No | Yes | No |
Special Cases
cpp
// Large read-only file content — string_view slices avoid copies
std::string content = read_file("big.txt");
auto process_line = [](std::string_view line) { /* ... */ };
for (auto line : split_lines(content)) // views into content
process_line(line);
// Thread-safe string sharing — shared_ptr<const string>
auto shared = std::make_shared<const std::string>(load_config());
// multiple threads read via shared_ptr — no copies
// Path handling — std::filesystem::path (not string)
std::filesystem::path p = "/usr/local/bin";
p /= "myapp"; // path concatenation
// Internationalization — std::u8string, std::u16string, std::u32string
std::u8string utf8 = u8"こんにちは";