Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template instantiation with constexpr function failure

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 ?

like image 338
Jean-Baptiste Yunès Avatar asked Dec 10 '14 20:12

Jean-Baptiste Yunès


People also ask

What is if constexpr in C++?

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).

Can a constexpr function call a non constexpr function?

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.

Can a function return constexpr?

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.

Is constexpr always 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.


2 Answers

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.

like image 189
Shafik Yaghmour Avatar answered Oct 05 '22 16:10

Shafik Yaghmour


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)

like image 24
Shoe Avatar answered Oct 05 '22 17:10

Shoe