As user17732522 already noted, the type deduction for your original code produces a const std::array<const char*, 3>
. This works, but it's not a C++ std::string
, so every use needs to scan for the NUL
terminator, and they can't contain embedded NUL
s. I just wanted to emphasize the suggestion from my comment to use std::string_view
.
Since std::string
inherently relies on run-time memory allocation, you can't use it unless the entirety of the associated code is also constexpr
(so no actual string
s exist at all at run-time, the compiler computes the final result at compile-time), and that's unlikely to help you here if the goal is to avoid unnecessary runtime work for something that is partially known at compile time (especially if the array
gets recreated on each function call; it's not global or static
, so it's done many times, not just initialized once before use).
That said, if you can rely on C++17, you can split the difference with std::string_view
. It's got a very concise literal form (add sv
as a prefix to any string literal), and it's fully constexpr
, so by doing:
// Top of file
#include <string_view>
// Use one of your choice:
using namespace std::literals; // Enables all literals
using namespace std::string_view_literals; // Enables sv suffix only
using namespace std::literals::string_view_literals; // Enables sv suffix only
// Point of use
constexpr std::array myStrings = { "one"sv, "two"sv, "three"sv };
you get something that involves no runtime work, has most of the benefits of std::string
(knows its own length, can contain embedded NUL
s, accepted by most string-oriented APIs), and therefore operates more efficiently than a C-style string for the three common ways a function accepts string data:
- For modern APIs that need to read a string-like thing, they accept
std::string_view
by value and the overhead is just copying the pointer and length to the function
- For older APIs that accept
const std::string&
, it constructs a temporary std::string
when you call it, but it can use the constructor that extracts the length from the std::string_view
so it doesn't need to prewalk a C-style string with strlen
to figure out how much to allocate.
- For any API that needs a
std::string
(because it will modify/store its own copy), they're receiving string
by value, and you get the same benefit as in #2 (it must be built, but it's built more efficiently).
The only case where you do worse by using std::string_view
s than using std::string
is case #2 (where if the std::array
contained std::string
s, no copies would occur), and you only lose there if you make several such calls; in that scenario, you'd just bite the bullet and use const std::array myStrings = { "one"s, "two"s, "three"s };
, paying the minor runtime cost to build real string
s in exchange for avoiding copies when passing to old-style APIs taking const std::string&
.
operator""s
forstd::string
isconstexpr
since C++20 so presumablyconstexpr
could be used with a new enough compiler. Or since C++17, they could usesv
suffix to make them allstd::string_view
literals (which wereconstexpr
from the start). – Niobic