Reputation: 1329
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.
Upvotes: 4
Views: 1114
Reputation: 158499
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.
Upvotes: 4
Reputation: 60999
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 typeT
, 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 ofe
[…] 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.
Upvotes: 2