I'm getting a strange compiler error when trying to create constexpr
std::string
and std::vector
objects:
#include <vector>
#include <string>
int main()
{
constexpr std::string cs{ "hello" };
constexpr std::vector cv{ 1, 2, 3 };
return 0;
}
The compiler complains that "the expression must have a constant value":
Am I missing something? I am using the latest Microsoft Visual Studio 2019 version: 16.11.4, and the reference (https://en.cppreference.com/w/cpp/compiler_support) states that constexpr
strings and vectors are supported by this compiler version:
I have also tried the constexpr std::array
, which does work. Could the issue have anything to do with the dynamic memory allocation associated with vectors?
constexpr started small in C++11 but then, with each Standard revision, improved considerably. In C++20, we can say that there's a culmination point as you can even use std::vector and std::string in constant expressions!
Formally, that's because vector constructor is not declared constexpr .
constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.
No, no such thing exists in C.
Your program is actually ill-formed, though the error may be hard to understand. constexpr
allocation support in C++20 is limited - you can only have transient allocation. That is, the allocation has to be completely deallocated by the end of constant evaluation.
So you cannot write this:
int main() {
constexpr std::vector<int> v = {1, 2, 3};
}
Because v
's allocation persists - it is non-transient. That's what the error is telling you:
<source>(6): error C2131: expression did not evaluate to a constant
<source>(6): note: (sub-)object points to memory which was heap allocated during constant evaluation
v
can't be constant because it's still holding on to heap allocation, and it's not allowed to do so.
But you can write this:
constexpr int f() {
std::vector<int> v = {1, 2, 3};
return v.size();
}
static_assert(f() == 3);
Here, v
's allocation is transient - the memory is deallocated when f()
returns. But we can still use a std::vector
during constexpr
time.
As @barry explained, you cannot create variables which requires dynamic allocation and which will be still available at runtime. I believe that this is explained by the following exclusion in :
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
https://eel.is/c++draft/expr.const#5.17
a new-expression ([expr.new]), unless the selected allocation function is a replaceable global allocation function ([new.delete.single], [new.delete.array]) and the allocated storage is deallocated within the evaluation of E;
Still you can do amazing things with this new features. For example join strings:
constexpr std::string join(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;
});
return result;
}
static_assert(join({ "one", "two", "three" }, ';') == "one;two;three"sv);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With