23

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?

  • Why the namespace_const_message and const_message aren't available at compile-time and thus forbidden in the print template function?
  • Is my guess about the string literals correct?

Thanks.

Community
  • 1
  • 1
PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94

3 Answers3

14

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.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 1
    Great answer about the linkage! nice one. I would like to ask you for more information about the linkage issue; AFAIK an unnamed namespace forces internal linkage and you say that *The instantiation variable of a template needed to have external linkage* so, why the `namespace_message` and `namespace_constexpr_message` are accepted in the template? Can you (please) extend your answer? – PaperBirdMaster Mar 02 '15 at 14:53
  • 2
    @PaperBirdMaster: That's not true about unnamed namespaces. It's clarified in C++11's footnote 94: _"Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit."_ The rules are given in 3.5/3 and 3.5/4. – Lightness Races in Orbit Mar 02 '15 at 16:19
  • @LightnessRacesinOrbit Ok, so I was wrong about the unnamed namespaces: they don't FORCE internal linkage, they COULD have internal linkage. Thanks for the clarification. – PaperBirdMaster Mar 03 '15 at 08:17
  • "Static class members always have external linkage" - although that statement was present in the C++11 standard wording, it is now considered incorrect according to [DR 1603](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1603); in the drafts after C++14, it was clarified that those names have the linkage of the name of their class. – bogdan Jun 03 '15 at 11:39
  • @LightnessRacesinOrbit That footnote is considered incorrect according to [DR 1603](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1603) and was removed in drafts after C++14. A note in the DR clearly states: *Names in unnamed namespaces never have external linkage.* (There's still a slight inconsistency related to that in the current draft, but I'm not going into it here.) Note that, as far as I understand, the DR considers the footnote incorrect even in the context of C++11. – bogdan Jun 03 '15 at 11:46
  • @bogdan It was correct in C++98 and C++03, since classes themselves always had external linkage (except local classes, but a local class couldn't have a static data member). – James Kanze Jun 08 '15 at 15:56
  • @JamesKanze Yes, but that doesn't make the statement correct in 2015, when it was written (the question is tagged `C++`, no version). I think it would a be a good idea to qualify some of the statements in there (about linkage and names) with the last version of the standard where they were valid, otherwise they could lead people astray, both now and in the future. – bogdan Jun 08 '15 at 17:25
4

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:

  1. 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
  2. the name of a non-type template-parameter; or
  3. 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
  4. a constant expression that evaluates to a null pointer value (4.10); or
  5. a constant expression that evaluates to a null member pointer value (4.11); or
  6. a pointer to member expressed as described in 5.3.1; or
  7. 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.

Community
  • 1
  • 1
hlscalon
  • 7,304
  • 4
  • 33
  • 40
  • It's irritating that that no reason is given for banning string literals, but +1 for quoting [the C++11 standard](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf) – Qwertie Feb 02 '19 at 20:18
2

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")>();
}
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93