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!
constexpr Containers and Algorithms of the Standard Template Library. 100 classical algorithms of the Standard Template Library are declared as constexpr . Consequently, you can sort a std::vector of ints at compile time.
Formally, that's because vector constructor is not declared constexpr .
So you should definitely use static constexpr in your example. However, there is one case where you wouldn't want to use static constexpr . Unless a constexpr declared object is either ODR-used or declared static , the compiler is free to not include it at all.
As of C++20, yes.
As of C++17, you can use string_view
:
constexpr std::string_view sv = "hello, world";
A string_view
is a string
-like object that acts as an immutable, non-owning reference to any sequence of char
objects.
No, and your compiler already gave you a comprehensive explanation.
But you could do this:
constexpr char constString[] = "constString";
At runtime, this can be used to construct a std::string
when needed.
C++20 will add constexpr
strings and vectors
The following proposal has been accepted apparently: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0980r0.pdf and it adds constructors such as:
// 20.3.2.2, construct/copy/destroy
constexpr
basic_string() noexcept(noexcept(Allocator())) : basic_string(Allocator()) { }
constexpr
explicit basic_string(const Allocator& a) noexcept;
constexpr
basic_string(const basic_string& str);
constexpr
basic_string(basic_string&& str) noexcept;
in addition to constexpr versions of all / most methods.
There is no support as of GCC 9.1.0, the following fails to compile:
#include <string>
int main() {
constexpr std::string s("abc");
}
with:
g++-9 -std=c++2a main.cpp
with error:
error: the type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} of ‘constexpr’ variable ‘s’ is not literal
std::vector
discussed at: Cannot create constexpr std::vector
Tested in Ubuntu 19.04.
Since the problem is the non-trivial destructor so if the destructor is removed from the std::string
, it's possible to define a constexpr
instance of that type. Like this
struct constexpr_str {
char const* str;
std::size_t size;
// can only construct from a char[] literal
template <std::size_t N>
constexpr constexpr_str(char const (&s)[N])
: str(s)
, size(N - 1) // not count the trailing nul
{}
};
int main()
{
constexpr constexpr_str s("constString");
// its .size is a constexpr
std::array<int, s.size> a;
return 0;
}
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";
}
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