Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference as a non-type template argument

The example below attempts to use a variable of reference type as an argument for a non-type template parameter (itself of reference type). Clang, GCC and VC++ all reject it. But why? I can't seem to find anything in the standard that makes it illegal.

int obj = 42;
int& ref = obj;

template <int& param> class X {};

int main()
{
    X<obj> x1;  // OK
    X<ref> x2;  // error
}

Live example


CLang says:

source_file.cpp:9:7: error: non-type template argument of reference type 'int &' is not an object

Others complain in similar ways.


From the standard (all quotes from C++11; C++14 doesn't appear to have significant changes in relevant parts):

14.3.2/1 A template-argument for a non-type, non-template template-parameter shall be one of:

...

  • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage ... expressed (ignoring parentheses) as & id-expression, except that the & ... shall be omitted if the corresponding template-parameter is a reference

...

Now what's a constant expression:

5.19/2 A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2)...

...

  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization, initialized with a constant expression

...


As far as I can tell, ref in X<ref> is an id-expression that refers to a variable of reference type. This variable has a preceding initialization, initialized with the expression obj. I believe obj is a constant expression, and anyway if it isn't, then X<obj> shouldn't compile either.

  • So what am I missing?
  • Which clause in the standard renders X<ref> invalid, while X<obj> is valid?
like image 696
Igor Tandetnik Avatar asked Feb 22 '15 20:02

Igor Tandetnik


People also ask

Can we use non-type parameters as arguments template?

Non-type template arguments are normally used to initialize a class or to specify the sizes of class members. For non-type integral arguments, the instance argument matches the corresponding template parameter as long as the instance argument has a value and sign appropriate to the parameter type.

What are non-type parameter for templates?

A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument. A non-type parameter can be any of the following types: An integral type. An enumeration type.

What is a template argument?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Which is correct example of template parameters?

For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.


1 Answers

Introduction

It is correct saying that the name of a reference is an id-expression, though; the id-expression doesn't refer to whatever the reference is referencing, but the reference itself.

 int    a = 0;
 int& ref = a; // "ref" is an id-expression, referring to `ref` - not `a`

The Standard (N4140)

You are quoting the relevant sections of the standard in your post, but you left out the most important part (emphasize mine):

14.3.2p1 Template non-type arguments [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter shall be one of:

  • ...

  • a constant expression (5.19) that designates the address of a complete object with static sturage 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, where id-expression is the name of an object or function, 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; ...


Note: In earlier drafts "where id-expression is the name of an object or function" isn't present; it was addressed by DR 1570 - which undoubtedly makes the intent more clear.


A variable of reference type is not an object?

You are absolutely correct; the reference itself has reference type, and can merely act as an object when part of an expression.

5p5 Expressions [expr]

If an expression initially has the type "reference to T" (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.


Elaboration

It is very important to note that the constant expression ("that designates the address of a complete object...") must be one of &id-expression, or id-expression.

Even though a constant-expression, that isn't just an id-expression, might refer to an object with static storage duration, we cannot use it to "initialize" a template-parameter of reference- or pointer type.

Example Snippet

template<int&>
struct A { };

int            a = 0;
constexpr int& b = (0, a); // ok, constant-expression
A<(0, a)>      c = {};     // ill-formed, `(0, a)` is not an id-expression

Note: This is also a reason behind the fact that we cannot use string-literals as template-arguments; they are not id-expressions.

like image 112
Filip Roséen - refp Avatar answered Nov 23 '22 03:11

Filip Roséen - refp