Reputation: 4854
I'm trying to write some compile time string composing functionality. The following code compiles if you comment out the code section at the end.
#include <iostream>
constexpr const char a[] = "hello ";
constexpr const char b[] = "dear ";
constexpr const char c[] = "world!";
constexpr size_t size(const char* s)
{
int i = 0;
while(*s!=0) {
++i;
++s;
}
return i;
}
template <typename... Is, typename = std::enable_if_t<(... && std::is_same_v<const char*, Is>)>>
constexpr size_t calc_size(Is... values) {
return (0 + ... + size(values));
}
constexpr bool strings_equal(char const * a, char const * b) {
return *a == *b && (*a == '\0' || strings_equal(a + 1, b + 1));
}
template <const char*... S>
class cxpr_string
{
public:
constexpr cxpr_string() : buf_{}, size_{0} {
int i=0;
( [&]() {
const size_t max = size(S);
for (int i=0; i < max; ++i) {
buf_[size_++] = S[i];
}
}(), ...);
buf_[size_++] = 0;
}
constexpr const char* get() const {
return buf_;
}
private:
char buf_[calc_size(S...)+1] = { 0 };
size_t size_;
};
template <const char*... ptr>
constexpr auto joined = cxpr_string<ptr...>().get();
int main()
{
static_assert(strings_equal(cxpr_string<a,b,c>().get(), "hello dear world!"));
std::cout << joined<a,b,c> << std::endl; // <-- Why not constexpr?? (comment this out)
std::cout << cxpr_string<a, b, c>().get() << std::endl;
}
But if you don't you get the following error:
<source>: In instantiation of 'constexpr const char* const joined<(& a), (& b), (& c)>':
<source>:56:18: required from here
<source>:50:16: error: '(const char*)(&<anonymous>.cxpr_string<(& a), (& b), (& c)>::buf_)' is not a constant expression
50 | constexpr auto joined = cxpr_string<ptr...>().get();
| ^~~~~~
Which is very surprising to me as the exact same application of the cxpr_string class as was used to initialize the variable in question passed the static_assert! Also there are compiler differences: It doesn't compile in gcc 12.1 and clang 14.0.0, but it compiles (but fails) in msvc 19.31.
What am I missing?
Upvotes: 1
Views: 183
Reputation: 120079
Here is a kinder, gentler reproduction of the problem.
Clang tells you what the problem is, sort of. "Pointer to subobject of temporary is not a constant expression". Indeed, where do you want joined
to point at?
A constexpr variable must be initialised by a constant expression, which foo().get()
is not. However it is a core constant expression. Indeed:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine (6.9.1), would evaluate one of the following:
- [...]
- an lvalue-to-rvalue conversion (7.3.1) unless it is applied to
- [...]
- a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E
However
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:
- [...]
- if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (7.6.6), the address of a non-immediate function, or a null pointer value
I guess one can pass a core constant expression to a constexpr function, and the result might be (but not always is) a full honest constant expression, suitable for initialising a constexpr variable.
Upvotes: 1