Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error when explicitly converting a template non-type parameter

Tags:

c++

templates

Consider the code:

class Base{};
class Derived: public Base{};

template<Base& b> // references (and pointers) can be used as non-types
void f(){}

int main()
{
    Derived d;
    // f(d); // Error, template type must match exactly
    f<(Base&)d>(); // Error here, why?!
}

I understand why the commented call fails: the template type must match exactly. I try however a cast in the second call, and get this error (gcc5.2):

error: 'd' is not a valid template argument for type 'Base&' because it is not an object with external linkage

Same error if I make Derived d; global. clang is a bit more helpful, saying

... note: candidate template ignored: invalid explicitly-specified argument for template parameter 'b'

My question is: is the code above legal or not? If not, are there any reasons why?

like image 526
vsoftco Avatar asked Sep 25 '15 18:09

vsoftco


2 Answers

This answer assumes C++11 or higher

Two issues here:

1) No derived-to-base conversion for non-type template parameter [temp.arg.nontype]/p1

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

2) The object's address should be available at compile time. Summarizing [temp.arg.nontype]/p1 and [expr.const]/p5 it follows that it should have static storage duration.

Put these two points together and you'll have that the following compiles

class Base{};
class Derived: public Base{};

template<Base& b> // references (and pointers) can be used as non-types
void f(){}

Base obj; // Static storage duration

int main()
{
    f<obj>();
}

Live Example

like image 78
Marco A. Avatar answered Sep 28 '22 08:09

Marco A.


From [temp.arg.nontype]:

A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter.

There are two issues here. First, d doesn't have linkage so you can't take a reference to it in a constant expression. That's an easy fix though:

Derived d;
int main() {
    f<d>(); // still an error
}

Now, we have another issue. We move onto the next sentence:

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):
(1.1) — a subobject (1.8),

We're trying to take a reference is a subobject (a base class subobject) of Derived. That is explicitly disallowed, regardless of linkage.

like image 30
Barry Avatar answered Sep 28 '22 10:09

Barry