Skip to content
C++

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 misuse

std::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 wantParameter type
Read-only, any sourcestd::string_view
Store/own (sink)std::string by value
Modify in-placestd::string&
C API interopconst char*
Compile-time onlystd::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::stringstd::string_viewconst char*
Owns dataYesNoNo
MutableYesNoNo*
Size knownYesYesNo (strlen)
Null-terminatedYesNo (may not be)Yes
Heap allocationYes (usually)NeverNever
SSO (short string opt)YesN/AN/A
Range-forYesYesNo
std::formatYesYesYes
C API (needs \0).c_str()Not safe (no \0)Yes
C++17 requirementNoYesNo

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"こんにちは";