I was playing with strings in templates. I read some interesting blogs and started playing with code.
In the next example, I was able to pass a string literal in a template argument:
#include <algorithm>
#include <iostream>
template<size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) {
// commenting this makes the program to not compile
std::copy_n(str, N, value);
}
constexpr size_t size() const
{
return N;
}
// changing N to literal, such as 10, makes the program to not compile
char value[N];
};
template<StringLiteral lit>
void Print() {
static constexpr auto size = lit.size();
static constexpr auto contents = lit.value;
std::cout << "Size: " << size << ", Contents: " << contents << std::endl;
}
int main()
{
Print<"abc">();
}
In this example, I'm passing a string to Print
by a template parameter, and it will print the size and value. So far so good.
However, I'm using some features that I don't fully understand, that's probably why I'm so confused.
- If I remove
std::copy_n()
, it doesn't compile. That was very surprising because that code is inside the constructor,N
was deduced just before, so I'm not sure whycopy_n
had these implications. - Changing
char value[N]
tochar value[10]
also makes the program invalid. 10 is a literal, so I was expecting the compiler would be able to deduce it without a problem.
Could you clarify what's going on?
CodePudding user response:
Both errors are caused by value
remaining partially uninitialized. Adding {}
to zero all elements by default is one way to solve this: char value[N]{};
.
CodePudding user response:
When you work with classes in a constexpr
context, it is important to guarantee the initialization.
You don't need to set a value, but at least saying : value()
will make it clear that the member is initialized:
constexpr StringLiteral(const char (&str)[N]) : value() {
}
If you leave the constructor empty without the : value()
, you leave value
uninitialized, which causes problems within the constexpr
context.