I have template class C
that has a non-type but reference template parameter to a type P
:
class P {
public:
int x;
int y;
};
template <const P &x>
class C {
public:
const int &f() { return x.x; }
};
I declared a global variable of type P
:
P p = {33,44};
I also declared a function that returns a reference to p
:
constexpr const P &h() { return p; }
And then tried to use these in the following :
C<p> o; // line 1
C<h()> oo; // line 2
Of course I have no problem with the first instantiation but the second. My compiler complains:
error: non-type template argument does not refer to any declaration
Why is so ? I was unable to find an argument against it in the norm. I am not sure that it is exactly the same problem as in Calling constexpr in default template argument, where the discussion was about point of instantiation of nested instanciation. Here it is more a type problem, but which one ? My function h()
returns a reference to a well defined variable of the well defined type (const P &
). I expected that some inlining would take place a give the right result but it is not the case. Could you tell me why ?
Declaring the function as inline doesn't change anything to the problem.
Experiments were done with Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn
. I also tried with g++-mp-4.8 (MacPorts gcc48 4.8.3_2) 4.8.3
and the error was reported as:
'h()' is not a valid template argument for type 'const P&' because it is not an object with external linkage
It looks like my call to h()
(which is a constexpr
so compile-time computable) is not seen as such...
I forgot to say that the problem is the same if we try with another reference like this:
const P &pp = p;
and then
C<pp> oo;
this time the first compiler says:
non-type template argument of reference type 'const P &' is not an object
and the second:
error: could not convert template argument 'pp' to 'const P &'
pp
is not an object? pp
is not of type const P&
? Well I can use it as is it one... I know it is a reference but indistinguishable from a native reference, or ?
In a constexpr if statement, the value of condition must be a contextually converted constant expression of type bool (until C++23)an expression contextually converted to bool, where the conversion is a constant expression (since C++23).
A call to a constexpr function produces the same result as a call to an equivalent non- constexpr function , except that a call to a constexpr function can appear in a constant expression. The main function cannot be declared with the constexpr specifier.
Unlike const , constexpr can also be applied to functions and class constructors. constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time.
A constexpr (which is short for “constant expression”) variable can only be a compile-time constant. If the initialization value of a constexpr variable is not a constant expression, the compiler will error.
It looks like this restriction was subject to the following proposal Allow constant evaluation for all non-type template arguments, still trying to determine the status of this proposal. It says:
The syntactic restrictions for pointers, references, and pointers to members are awkward and prevent reasonable refactorings. For instance:
template<int *p> struct A {}; int n; A<&n> a; // ok constexpr int *p() { return &n; } A<p()> b; // error
and further says:
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 status quo is that an implementation is required to evaluate such a template argument, but must then discard the result if it turns out to not be null.
In addition to the above, the restriction to entities with linkage is an artifact of exported templates, and could have been removed when the linkage restrictions on template type parameters were removed.
and it would remove this section of the note with this restriction:
unnamed lvalues, and named lvalues with no linkage
the whole note reads:
Temporaries, unnamed lvalues, and named lvalues with no linkage are not acceptable template-arguments when the corresponding template-parameter has reference type.
Update
The revised version of this proposal N4268 was adopted into the working draft at Urbana and we can see the changes in the latest working draft N4296. The new note reads:
A temporary object is not an acceptable template-argument when the corresponding template-parameter has reference type
The normative section is 14.3.2
(temp.arg.nontype) paragraph 1
which with this proposal would say:
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.14.5),
- the result of a typeid expression (5.2.8), or
- a predefined func variable (8.4.1).
and we can find this new wording in the latest draft standard N4296.
It looks like this change has actually been implemented in clang HEAD
see your code working live, using the -std=c++1z
flag. This implies the change should be part of C++17, assuming no subsequent changes reverses or alters it.
In the case of
C<h()> oo;
§14.3.2/4 kicks in:
[Note: Temporaries, unnamed lvalues, and named lvalues with no linkage are not acceptable template- arguments when the corresponding template-parameter has reference type.
(emphasis mine)
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