C++20 is a step toward making it possible to use std::string
at compile time, but P0980 will not allow you to write code like in your question:
constexpr std::string constString = "constString";
the reason is that constexpr
std::string
is allowed only to be used in constexpr
function (constant expression evaluation context). Memory allocated by constexpr
std::string
must be freed before such function returns - this is the so called transient allocation, and this memory cannot 'leak' outside to runtime to constexpr
objects (stored in data segments) accessible at runtime . For example compilation of above line of code in current VS2022 preview (cl version : 19.30.30704) results in following error:
1> : error C2131: expression did not evaluate to a constant
1> : message : (sub-)object points to memory which was heap allocated during constant evaluation
this is because it tries to make a non-transient allocation which is not allowed - this would mean allocation into a data segment of the compiled binary.
In p0784r1, in "Non-transient allocation" paragraph, you can find that there is a plan to allow conversion of transient into static memory (emphasis mine):
What about storage that hasn't been deallocated by the time evaluation
completes? We could just disallow that, but there are really
compelling use cases where this might be desirable. E.g., this could
be the basis for a more flexible kind of "string literal" class. We
therefore propose that if a non-transient constexpr allocation is
valid (to be described next), the allocated objects are promoted to
static storage duration.
There is a way to export transient std::string
data outside to make it usable at runtime. You must copy it to std::array
, the problem is to compute the final size of std::array
, you can either preset some large size or compute std::string
twice - once to get size and then to get atual data. Following code successfully compiles and runs on current VS2022 preview 5. It basicly joins three words with a delimiter between words:
constexpr auto join_length(const std::vector<std::string>& vec, char delimiter) {
std::size_t length = std::accumulate(vec.begin(), vec.end(), 0,
[](std::size_t sum, const std::string& s) {
return sum + s.size();
});
return length + vec.size();
}
template<size_t N>
constexpr std::array<char, N+1> join_to_array(const std::vector<std::string>& vec, char delimiter) {
std::string result = std::accumulate(std::next(vec.begin()), vec.end(),
vec[0],
[&delimiter](const std::string& a, const std::string& b) {
return a + delimiter + b;
});
std::array<char, N+1> arr = {};
int i = 0;
for (auto c : result) {
arr[i++] = c;
}
return arr;
}
constexpr std::vector<std::string> getWords() {
return { "one", "two", "three" };
}
int main()
{
constexpr auto arr2 = join_to_array<join_length(getWords(), ';')>(getWords(), ';');
static_assert(std::string(&arr2[0]) == "one;two;three");
std::cout << &arr2[0] << "\n";
}