Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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 ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
270 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...