The warning is appropriate, just worded a bit unclear.
hello
is declared constexpr
, as well as redundantly const
. A const
variable generally has internal linkage (with some exceptions like variable templates, inline
variables, etc.).
Therefore hello
in the template argument to Base<hello>
is a pointer to an internal linkage object. In each translation unit it will refer to a different hello
local to that translation unit.
Consequently Base<hello>
will be different types in different translation units and so if you include child.hh
in more than one translation unit you will violate ODR on Child
's definition and therefore your program will have undefined behavior.
The warning is telling you that. The use of "anonymous namespace" is not good. It should say something like "whose type refers to an entity with internal linkage". But otherwise it is exactly stating the problem.
This happens only if you use a pointer or reference to the object as template argument. If you take just the value of a variable, then it doesn't matter whether that variable has internal or external linkage since the type Base<...>
will be determined by the value of the variable, not the identity of the variable.
I don't know why you need the template argument to be a pointer here, but if you really need it and you actually want to include the .hh
file in multiple .cc
files (directly or indirectly), then you need to give hello
external linkage. Then you will have the problem that an external linkage variable may only be defined in one translation unit, which you can circumvent by making the variable inline
:
inline constexpr char hello[] = "Hello world!";
inline
implies external linkage (even if the variable is const
) and constexpr
implies const
.
If you only need the value of the string, not the identity of the variable, then since C++20 you can pass a std::array<char, N>
as template argument:
#include<array>
#include<concepts>
/*...*/
template<std::array s>
requires std::same_as<typename decltype(s)::value_type, char>
class Base
{
public:
void print()
{
std::cout << s.data() << std::endl;
}
};
/*...*/
constexpr auto hello = std::to_array("Hello world!");
With this linkage becomes irrelevant. Writing Base<std::to_array("Hello world!")>
is also possible.
Before C++20 there isn't really any nice way to pass a string by-value as template argument.
Even in C++20 you might want to consider writing your own std::array
-like class specifically for holding strings. Such a class needs to satisfy the requirements for a structural type. (std::string
does not satisfy them.) On the other hand you might want to widen the allowed template parameter types by using the std::range
concept and std::range_iter_t
instead of std::array
to allow any kind of range that could represent a string. If you don't want any restrictions just auto
also works.