[ACCEPTED]-String literals not allowed as non type template parameters-string-literals

Accepted answer
Score: 54

Your compiler ultimately operates on things 35 called translation units, informally called source files. Within these translation units, you 34 identify different entities: objects, functions, etc. The 33 linkers job is to connect these units together, and 32 part of that process is merging identities.

Identifiers 31 have linkage: internal linkage means that the entity named in that 30 translation unit is only visible to that 29 translation unit, while external linkage means that the 28 entity is visible to other units.

When an 27 entity is marked static, it is given internal linkage. So 26 given these two translation units:

// a.cpp
static void foo() { /* in a */ } 

// b.cpp
static void foo() { /* in a */ } 

Each of 25 those foo's refer to an entity (a function 24 in this case) that is only visible to their 23 respective translation units; that is, each 22 translation unit has its own foo.

Here's the 21 catch, then: string literals are the same 20 type as static const char[..]. That is:

// str.cpp
#include <iostream>

// this code:

void bar()
{
    std::cout << "abc" << std::endl;
}

// is conceptually equivalent to:

static const char[4] __literal0 = {'a', 'b', 'c', 0};

void bar()
{
    std::cout << __literal0 << std::endl;
}

And as you can see, the 19 literal's value is internal to that translation 18 unit. So if you use "abc" in multiple translation 17 units, for example, they all end up being 16 different entities.

Overall, that means this 15 is conceptually meaningless:

template <const char* String>
struct baz {};

typedef baz<"abc"> incoherent;

Because "abc" is 14 different for each translation unit. Each translation 13 unit would be given a different class because each 12 "abc" is a different entity, even though they 11 provided the "same" argument.

On 10 the language level, this is imposed by saying 9 that template non-type parameters can be 8 pointers to entities with external linkage; that is, things 7 that do refer to the same entity across translation 6 units.

So this is fine:

// good.hpp
extern const char* my_string;

// good.cpp
const char* my_string = "any string";

// anything.cpp
typedef baz<my_string> coherent; // okay; all instantiations use the same entity

†Not all identifiers 5 have linkage; some have none, such as function 4 parameters.

‡ An optimizing compiler will 3 store identical literals at the same address, to 2 save space; but that's a quality of implementation 1 detail, not a guarantee.

Score: 11

It means you can't do this...

#include <iostream>

template <const char* P>
void f() { std::cout << P << '\n'; }

int main()
{
    f<"hello there">();
}

...because 7 "hello there" isn't 100% guaranteed to resolve to a single 6 integral value that can be used to instantiate 5 the template once (though most good linkers 4 will attempt to fold all usages across linked 3 objects and produce a new object with a 2 single copy of the string).

You can, however, use 1 extern character arrays/pointers:

...
extern const char p[];
const char p[] = "hello";
...
    f<p>();
...
Score: 7

Obviously, string literals like "foobar" are 29 not like other literal built-in types (like 28 int or float). They need to have an address 27 (const char*). The address is really the 26 constant value that the compiler substitutes 25 in place of where the literal appears. That 24 address points to somewhere, fixed at compile-time, in 23 the program's memory.

It has to be of internal 22 linkage because of that. Internal linkage 21 just means that cannot be linked across 20 translation units (compiled cpp files). The 19 compiler could try to do this, but is not 18 required to. In other words, internal linkage 17 means that if you took the address of two 16 identical literal strings (i.e. the value 15 of the const char* they translate to) in 14 different cpp files, they wouldn't be the 13 same, in general.

You can't use them as template 12 parameters because they would require a 11 strcmp() to check that they are the same. If 10 you used the ==, you would just be comparing 9 the addresses, which wouldn't be the same 8 when template are instantiated with the 7 same literal string in different translation 6 units.

Other simpler built-in types, as 5 literals, are also internal linkage (they 4 don't have an identifier and can't be linked 3 together from different translation units). However, their 2 comparison is trivial, as it is by value. So 1 they can be used for templates.

Score: 3

As mentioned in other answers, a string 11 literal cannot be used as a template argument. There 10 is, however, a workaround which has a similar 9 effect, but the "string" is limited 8 to four characters. This is due to multi-character constants which, as 7 discussed in the link, are probably rather 6 unportable, but worked for my debug purposes.

template<int32_t nFourCharName>
class NamedClass
{
    std::string GetName(void) const
    {
        // Evil code to extract the four-character name:
        const char cNamePart1 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*3) & 0xFF);
        const char cNamePart2 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*2) & 0xFF);
        const char cNamePart3 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*1) & 0xFF);
        const char cNamePart4 = static_cast<char>(static_cast<uint32_t>(nFourCharName       ) & 0xFF);

        std::ostringstream ossName;
        ossName << cNamePart1 << cNamePart2 << cNamePart3 << cNamePart4;
        return ossName.str();
    }
};

Can 5 be used with:

NamedClass<'Greg'> greg;
NamedClass<'Fred'> fred;
std::cout << greg.GetName() << std::endl;  // "Greg"
std::cout << fred.GetName() << std::endl;  // "Fred"

As I said, this is a workaround. I 4 don't pretend this is good, clean, portable 3 code, but others may find it useful. Another 2 workaround could involve multiple char template 1 arguments, as in this answer.

Score: 0

Idea of c++ standard only allowing certain 13 type of parameters to the templates is that 12 parameter should be constant and known at 11 compile time in order to generate "specialized 10 class" code.

For this specific case: When 9 you create string literal their address 8 is unknown until linking time (linking happens 7 after compilation) because two string literals 6 across different translation units are two 5 different objects (as explained brilliantly 4 by accepted answer). When compilation happens 3 we don't know which string literal's address 2 to use to generate the specialized class 1 code from template class.

More Related questions