So, I was playing with strings in templates, I saw some interesting blogs and started playing with. 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 just be deduced before, so, I'm not sure why copy_n had these implcations.
- Changing
char value[N]
tochar value[10]
also makes the program invalid, 10 it's a literal, 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 error 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 members is initialized.
constexpr StringLiteral(const char (&str)[N]) : value() {
}
If you let the constructor empty without the : value()
, you leave value
unitialized, which causes problems within the constexpr
context.