Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the common template method definition doesn't match the template class specialization?

I have the following code:

template <class T, class U = T>
class A {
  public:
    void f();
};

template <class T>
class A<T, T> {
  public:
    void f();  // Unaltered method.

    // Some differences.
};

template <class T, class U>
void A<T, U>::f() {}

int main() {
  A<int> a;
  a.f();
  return 0;
}

The clang++ -std=c++11 test.cc gives me an error: undefined reference to 'A<int, int>::f()'

Why the provided definition of method f() doesn't apply to the class A<int, int>?

like image 929
abyss.7 Avatar asked Jan 08 '14 04:01

abyss.7


2 Answers

The primary class template template <class T, class U = T> class A and the partial specialization template <class T> class A<T, T> are two distinct template definitions. After they've been defined, whenever you refer to the class template name A, the primary template and all partial specializations will always be considered.

Whenever you instantiate A with either a single template argument, or two arguments of the same type, it'll form a better match for the specialization you've provided, and the primary template is not considered.

In your example, because of the partial specialization you've provided, there's no way to match the primary template, regardless of the default template argument, if you try to instantiate A with a single template argument, or two of the same type.

The solution, of course, is to provide the definition for A<T, T>::f()

template <class T>
void A<T, T>::f() {}

EDIT:
In the presence of partial specializations, the rules for matching them are given by (from N3797) §14.5.5.1/1 [temp.class.spec.match]

When a class template is used in a context that requires an instantiation of the class, it is necessary to determine whether the instantiation is to be generated using the primary template or one of the partial specializations. This is done by matching the template arguments of the class template specialization with the template argument lists of the partial specializations.
— If exactly one matching specialization is found, the instantiation is generated from that specialization.
— If more than one matching specialization is found, the partial order rules (14.5.5.2) are used to determine whether one of the specializations is more specialized than the others. ...
— If no matches are found, the instantiation is generated from the primary template.

In your example the first rule applies, and the compiler doesn't even get to the 3rd rule.

like image 61
Praetorian Avatar answered Nov 06 '22 23:11

Praetorian


When you define a member function of a class template outside the class, you are just defining the function for the corresponding function that was declared in the class template. You are not creating a new function template which might match other parameters. In your example:

template <class T, class U = T>
class A {
  public:
    void f(); // This is the declaration of A<T,U>::f()
};

template <class T>
class A<T, T> {
  public:
    void f();  // This is the declaration of A<T,T>::f()
};

template <class T, class U>
void A<T, U>::f() {} // This is the definition of A<T,U>::f()

// There is no definition of A<T,T>::f()

I believe what you are thinking is that the compiler will see that you are calling A<int,int>::f() and will look through the member function definitions and find one that matches, but this is not what happens. The compiler always looks through the class templates to find which function to call, and once it has found a match, it then looks for the corresponding definition. In your case, you are calling A<int,int>::f(), so it first looks for a class definition that matches A<int,int> and it finds your A<T,T> class template specialization. It sees that A<T,T> does indeed have a member function called f which matches your function call, which means A<T,T>::f() needs to be instantiated. To instantiate A<T,T>::f(), the compiler looks for the definition of A<T,T>::f(), however it doesn't find it. It only finds the definition of A<T,U>::f, which isn't a match. The template parameter matching that is used to find a proper function declaration doesn't apply.

like image 36
Vaughn Cato Avatar answered Nov 06 '22 23:11

Vaughn Cato