1
#include <iostream>
using namespace std;

template<class T, int I>  // primary template
struct A {
    void f(); // member declaration
};
 
template<class T, int I>
void A<T,I>::f() { } // primary template member definition
 
// partial specialization
template<class T>
struct A<T,2> {
    void f();
    void g();
    void h();
};
 
// member of partial specialization
template<class T>
void A<T,2>::g() { 
    cout << "partial g()" << endl;
}

template<class T>
void A<T,2>::h() { 
    cout << "partial h()" << endl;
}
 
// explicit (full) specialization
// of a member of partial specialization
template<>
void A<char,2>::h() {
    cout << "explicit h()" << endl;
}
 
int main() {
    A<char,2> a2;
    a2.f(); // ERROR, partial can not access primary member
    a2.g(); // OK, uses partial specialization's member definition
    a2.h();  // OK, explicit h() being called.
}

I went thorugh cpp reference, It says

"Members of partial specializations are not related to the members of the primary template."

So understandably, a2 can not access member of primary specialization a2.f() ?

My Questions are

  1. how is the relationship between member of partial specialization and member of explicit specialization ?

  2. Why a2 can access the member of partial specialization a2.g() here ?

sthbuilder
  • 561
  • 1
  • 7
  • 22
  • 1) There is no relationship. 2) Because it's defined? P.S. a2.f(); is declared, but not defined, so the code compiles just fine, but fails to link due to undefined template member. – Sam Varshavchik Jan 07 '21 at 18:45
  • @Sam Thx. if there is no relationship, what is the cause that when we do `a2.h()`, the explicit one is called, not the one in the partial specialization ? – sthbuilder Jan 07 '21 at 19:00
  • Why shouldn't it? `A::f()` is explicitly specialized, so it get's called. It is a member of class `A`. – Sam Varshavchik Jan 07 '21 at 19:15

1 Answers1

1

It is important to realize that a class template is not a class, but only a blueprint for one.

A<char,2> matches the specialized blueprint, so it has f,g,h member functions. Contents of the primary template are completely ignored.

Because A is a template class, its methods are instantiated only when actually called and only for those concrete template arguments because instantiation of A is a class, thus it has only one set of methods.

  • Since you did not define any A<char,2>::f, the linker reports undefined reference error.
  • There is a definition of A<char,2>::g available by instantiating template<class T> void A<T,2>::g() function - that is an ordinary method, not a function template.
  • For the same reason, A<char,2>::h also compiles - there is a definition for this function, nothing more is required.

In a nutshell, the compiler will only look for the definitions it needs, if it finds a matching template, it will generate the definitions. Otherwise it will mark the symbol as missing and it is your responsibility that the linker will have it available.

Quimby
  • 17,735
  • 4
  • 35
  • 55
  • this mostly makes sense, thx. Maybe a follow up question is why `a2.h(); // OK, explicit h() being called. ` instead of the partial one ? – sthbuilder Jan 07 '21 at 18:57
  • @sthbuilder Can you explain a little bit more please? What is unclear about `a2.h()` - like why it works? Because the compiler was able to find a template that can generate its definition. – Quimby Jan 07 '21 at 19:00
  • sorry for the confusion, if 1. A matches the specialized blueprint 2. there is no relationship between partial specialization and explicit specialization member. My question is why when we do `a2.h()`, the explicit h() is called instead ? – sthbuilder Jan 07 '21 at 19:02
  • Right, I get it now, sorry. Because that is how the overload call resolution rules work - the most specialized template is selected. – Quimby Jan 07 '21 at 19:02
  • Isn't this for template function resolution as in https://stackoverflow.com/questions/22411482/c-template-functions-overload-resolution? In my case, it is ordinary member function of class template, right ? – sthbuilder Jan 07 '21 at 19:07
  • `A` is bettern match than `A` for the type of `a2`, so its template blueprint is used. There is a relationship between `A::h()` and `A::h()`, they come from the same class template. What you posted in your question means there is no relationship between `A::f` and `A::f`. – Quimby Jan 07 '21 at 19:09
  • This is slightly confusing, the language simply allows to only define a non-template function for some particular specialization of class-template arguments, in this case you can define `A::f` only for the case of `A::f`, it is also chosen over the general `A::f` definition if you include it as well. The reason I believe is to eliminate the need to specialize `A` template itself to `A` which would differ only in `f`'s implementation but would require redefining everything in since it is independent of `A` – Quimby Jan 07 '21 at 19:11
  • Also meaning that if you add `template<> struct A{};`, the explicit `h` will stop working, because it will think it belongs to `A` which did not declare any `h`. – Quimby Jan 07 '21 at 19:15
  • Confirming if I understand it correctly, you are saying the right process is 1. Find the most specialized class template blueprint (In this case, both use the partial specialization) 2. If the class template used are the same, the overload resolution come into play. Is this correct ? – sthbuilder Jan 07 '21 at 19:16
  • Yes, exactly, first the correct class template is found - `A`, then the method call is resolved w.r.t to its parameters. Then its definition is searched for - `A::h` and `A::h` are two function templates candidates which can generate such definition, since the latter is more specialized, it is chosen. I think I misspoke, it is probably not called overload resolution, not sure about that, sorry. But that is how it works. Perhaps someone else can give you the proper name. – Quimby Jan 07 '21 at 19:25