102

I understand that non-type template parameters should be a constant integral expression. Can someone shed light why this is so?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.

I understand what a constant integral expression is. What are the reasons for not allowing non-constant types like std::string as in the above snippet ?

codeling
  • 11,056
  • 4
  • 42
  • 71
Mahesh
  • 34,573
  • 20
  • 89
  • 115

4 Answers4

129

The reason you can't do this is because non-constant expressions can't be parsed and substituted during compile-time. They could change during runtime, which would require the generation of a new template during runtime, which isn't possible because templates are a compile-time concept.

Here's what the standard allows for non-type template parameters (14.1 [temp.param] p4):

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

  • integral or enumeration type,
  • pointer to object or pointer to function,
  • lvalue reference to object or lvalue reference to function,
  • pointer to member,
  • std::nullptr_t.
Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 6
    @ALOToverflow: That falls under "pointer to member". It's either "pointer to member function" or "pointer to member data". – Xeo Apr 12 '13 at 16:55
  • 7
    It's worth noting that for the case of pointers to objects (or instance fields), the objects must have static storage duration and linkage (external pre C++11, internal or external in C++11), so that pointers to them can be created at compile time. – Theodore Murdock Jan 29 '16 at 20:40
  • 4
    In C++20 this is now allowed provided that the type has strong structured equality, is a literal, no mutable/volatile subobjects and where the spaceship operator is public. – Rakete1111 Sep 12 '18 at 13:30
  • Whether a type can be a non-type template parameter is orthogonal to the issue of changing things at runtime; `int` variables can change, but `int` NTTPs cannot. – Davis Herring Jun 22 '21 at 16:00
80

That is not allowed.

However, this is allowed:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

See §14.1/6,7,8 in C++ Standard (2003).


Illustration:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

Output:

can assign values locally
can assign values locally
can assign values locally...appended some string
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • @Nawaz - What is reason for not allowing the same concept for local variables ? I can't think for a reason for not allowing it. Thanks. – Mahesh Apr 16 '11 at 16:02
  • 7
    @Mahesh: Because what the template basically templates on is the address of that `std::string` pointer or reference object. If that variable was local, you would possibly get different addresses every time the function was called. – Xeo Apr 16 '11 at 16:16
  • @Xeo - Even if a different address is passed, required function that can take address is generated at the compile time. So, what is the problem ? Could you please explain it with an example ? – Mahesh Apr 16 '11 at 16:20
  • 11
    @Mahesh: You don't know what the call-stack looks like at compile time. Before your function, there could've been called 10 other or 3 others or however many other, so the address on the stack, at which the string gets created can change from call to call. When you have an object with external linkage, its address is fixed during compilation/linkage. – Xeo Apr 16 '11 at 16:56
  • 2
    @Xeo "_its address is fixed during compilation/linkage._" Or not, for relocatable or position-independent code. – curiousguy Jun 28 '13 at 01:07
  • 1
    This answer (currently) doesn't seem to address the OP's question, which was asking *why* this behavior exists; this answer merely restates the OP's example without offering any explanation. – Quuxplusone Apr 15 '17 at 22:03
  • 1
    I'm late for the party, it seems that smart pointers also do not work – Nicholas Humphrey Jul 03 '18 at 19:09
31

You need to be able to mangle template arguments

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"foo">(); // same function?

Now an impl would need to come up with a unique sequence of characters for a std::string or, for that matter, any other arbitrary user defined class, storing a particular value, the meaning of which is not known to the implementation. And in addition, the value of arbitrary class objects can't be calculated at compile time.

It's planned to consider allowing literal class types as template parameter types for post-C++0x (see below), which are initialized by constant expressions. Those could be mangled by having the data members recursively mangled according to their values (for base classes, for example we can apply depth-first, left-to-right traversal). But it's definitely not going to work for arbitrary classes.


As of C++20, we are now allowed to use structural class types as template parameters. In a nutshell, structural classes must have a constexpr constructor, destructor and only structural-type members and base classes (like scalars, arrays thereof or references). They must also only have public and non-mutable base classes and members. These provisions, if the template is instantiated with a constant expression converted to the parameter type, allow the compiler to mangle the argument meaningfully.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
10

A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be:

constant expressions, addresses of functions or objects with external linkage, or addresses of static class members.

Also, string literals are objects with internal linkage, so you can't use them as template arguments. You cannot use a global pointer, either. Floating-point literals are not allowed, given the obvious possibility of rounding-off errors.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Sadique
  • 22,572
  • 7
  • 65
  • 91