Home > Software engineering >  Compile surprises when using a string as a template argument
Compile surprises when using a string as a template argument

Time:12-23

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">();
}

Godbolt Link

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.

  1. 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 why copy_n had these implications.
  2. Changing char value[N] to char 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.

  • Related