Why is it that the template argument of a non-type reference cannot be another reference (g++ 4.8.1):
template <int& N> void test() { }
int x = 5;
int& p = x;
int main(){
test<x>(); //compiles fine
test<p>(); //error: could not convert template argument 'p' to 'int&'|
}
I can't see where from the standard p
is violating anything, these seemed the most relevant sections (N3337):
[14.3.2] [.1] A template-argument for a non-type, non-template template-parameter shall be one of:
— 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
— the name of a non-type template-parameter; or
— 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]
[ Note: Temporaries, unnamed lvalues, and named lvalues with no linkage are not acceptable templatearguments
when the corresponding template-parameter has reference type.
[.5]
— For a non-type template-parameter of type reference to object, no conversions apply. The type referred
to by the reference may be more cv-qualified than the (otherwise identical) type of the templateargument.
The template-parameter is bound directly to the template-argument, which shall be an
lvalue.
p
should be considered an lvalue shouldn't it? The only other thing I could think of was maybe a lack of linkage for references but adding extern int& p = x
didn't fix it either.
This is related to the previous question template instantiation with constexpr function failure which I linked to a while ago in the comments, although your case is different.
It looks the example was previously not allowed but support was added in to C++1z via the proposal Allow constant evaluation for all non-type template arguments which opens with:
the syntactic restrictions for pointers, references, and pointers to
members are awkward and prevent reasonable refactorings. [...] The
historical reason for the restriction was most likely that C++
previously did not have a sufficiently strong specification for
constant expressions of pointer, reference, or pointer-to-member type.
However, that is no longer the case. [...]
The specific changes that seems relevant to your case is the rewording of the draft C++ standard section 14.3.2
Template non-type arguments [temp.arg.nontype]/p1 from:
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 storage durationtion
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 the 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; or
[...]
to:
A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of
the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the
value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
a subobject (1.8),
a temporary object (12.2),
a string literal (2.13.5),
the result of a typeid expression (5.2.8), or
a predefined func variable (8.4.1).
and a change of section 5.20
Constant expressions [expr.const]/p4 has the following paragraph on converted constant expressions, which starts out:
A converted constant expression of type T is an expression, implicitly converted to type T, where the converted
expression is a constant expression and the implicit conversion sequence contains only
and this in particular was added:
[...] and where the reference binding (if any) binds directly [...]
Note, the current head version of clang compiles your code in C++1z mode, see it live.
The updated version of the N4268 was the one applied and clang C++1z implementation status section indicates this paper was support from clang 3.6. This code only works in C++1z mode for clang 3.6 and greater.
Using the simplified wording introduced by N4268 (and now in the WD),
A template-argument for a non-type template-parameter shall be a
converted constant expression (5.20) of the type of the template-parameter. For a non-type template-parameter of reference […] type, the
value of the constant expression shall not refer to […]: […cases that don't apply…]
"converted constant expression" is defined in [expr.const]/4:
A converted constant expression of type T
is an expression,
implicitly converted to type T
, where the converted expression is a
constant expression and the implicit conversion sequence contains
only […] and where the reference binding (if any) binds directly.
Clearly, the reference binds directly. Are x
and p
constant expressions in this context?
[expr.const]/5:
A constant expression is either a glvalue core constant expression
whose value refers to an entity that is a permitted result of a
constant expression (as defined below), or […]
A permitted result of a constant expression is defined in the next paragraph as
…an object with static storage duration that is either not a temporary object or […]
x
and p
do refer to an object with static storage duration, but are they core constant expressions in the given context? The answer is yes: As long as their value (or the value of the object p
refers to) is not examined by the expression, which it isn't, everything's fine, even for p
:
A conditional-expression e
is a core constant expression unless the evaluation of e
[…] would evaluate one of the following expressions:
— an id-expression that refers to a variable or data member of
reference type unless the reference has a preceding initialization and
either
- it is initialized with a constant expression
x
as the initializer of p
is a constant expression (just as it is a valid template-argument for int&
), hence p
as the template-argument is a constant expression as well.
Note that Clang as of version 3.6 compiles your snippet fine.