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.
X<ref>
invalid, while X<obj>
is valid?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.
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.
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.
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.
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`
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.
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 toT
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.
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.
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