Let's suppose we have a template function with non-type parameter of const char *
like this:
template <const char * MESSAGE> void print() {
std::cout << MESSAGE << '\n';
}
Using this template wouldn't be a problem as log as the MESSAGE
can be deduced at compile-time, so the following uses are legal:
namespace {
char namespace_message[] = "Anonymous Namespace Message";
constexpr char namespace_constexpr_message[] = "Anonymous Namespace Constexpr Message";
}
char message[] = "Message";
constexpr char constexpr_message[] = "Constexpr Message";
int main()
{
print<namespace_message>();
print<namespace_constexpr_message>();
print<message>();
print<constexpr_message>();
return 0;
}
But the ones below are not (see here):
namespace {
const char namespace_const_message[] = "Anonymous Namespace Const Message";
}
const char const_message[] = "Const Message";
int main()
{
print<namespace_const_message>();
print<const_message>();
print<"Literal">();
return 0;
}
The errors generated by the code above are the following:
the value of '{anonymous}::namespace_const_message' is not usable in a constant expression
I don't get why namespace_const_message
is not usable in a constant expression while namespace_message
is; if I must bet for one of them to be unable to be used in a constant expression I'll bet for the no constant one, but is the one which already works as constant expression!
note: '{anonymous}::namespace_const_message' was not declared 'constexpr'
namespace_message
was neither declared as constexpr
and is used into a constant expression and its value is deduced at compile time. Why constexpr
is needed if the expression is const
and not required if no-const?
Same goes for the values outside the anonymous namespace, I was trying to force the compile-time-constness placing the values into a internal linkage space but is obvious that I've failed.
Finally, the last error:
'"Literal"' is not a valid template argument for type 'const char*' because string literals can never be used in this context
So, surprisingly (at least it was a surprise for me) a string literal cannot be used as template argument, but as long as the string (well, a pointer to a null-terminated array of characters) is a compile-time value it can be used as non-type template parameters so: they're available at compile-time as long as "they are a lvalue" (but they're already lvalues!).
I'm trying to guess why a string literal can never be used in this context, and my best guess is that two string literals with the same content aren't the same literal (because the pointer which points to the content could be different) while two integral literals are the same (they're a value, not a pointer to a value).
So, what's the question here?
namespace_const_message
and const_message
aren't available at compile-time and thus forbidden in the print
template function?Thanks.
The instantiation variable of a template needed to have external
linkage, and const
was implicitly internal linkage. So you have to
write:
extern char const constMessage[] = "Const message";
(Another alternative would be for it to be a static class member. Static class members always have external linkage.)
The case of string literals is in some ways similar: their type is
char const[]
. But it's even worse: template instantiations (at least
the early ones) need a name, and a string literal doesn't have one.
Even more to the point, it's unspecified whether identical string literals
are the same object or not, so in the following:
template <char const* m>
struct Toto { char const* f() const; };
Toto <"titi"> t1;
Toto <"titi"> t2;
it would be unspecified whether t1
and t2
had the same type or not.
From the c++11 standard §14.3.2.1
Template non-type arguments
A template-argument for a non-type, non-template template-parameter shall be one of:
- for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
- the name of a non-type template-parameter; or
- a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
- a constant expression that evaluates to a null pointer value (4.10); or
- a constant expression that evaluates to a null member pointer value (4.11); or
- a pointer to member expressed as described in 5.3.1; or
- an address constant expression of type std::nullptr_t.
To your questions:
Why the namespace_const_message and const_message aren't available at compile-time and thus forbidden in the print template function?
That's why constexpr
exists. They can be used where it's needed compile-time evaluation, thus available to be template-arguments.
Is my guess about the string literals correct?
There is a note about this right after the arguments:
Note: A string literal (2.14.5) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.
You can also use a std::array<char, N>
. The sample below requires C++20:
#include <array> // std::array
#include <cstddef> // std::size_t
template <auto constexpr_string>
void needs_constexpr_string() {
// ... use the string ...
}
template <auto N>
consteval auto str(char const (&cstr)[N]) {
std::array<char, N> arr;
for (std::size_t i = 0; i < N; ++i)
arr[i] = cstr[i];
return arr;
}
int main() {
needs_constexpr_string<str("Hello World")>();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With