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?
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:However
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.