3

I have a base class with a template member, which is explicitly specialized for some cases. A derived class further specializes the template member of the base class. The rationale beyond that is that the various specialization of the template member do "logically" the same job, adapting to a specific situation. The base class provides some template specializations, which do the task for some cases, a derived class should "extend" the same task to other cases, by further specializing the template member.

Here is a minimal example to illustrate the problems I encounter.

#include <iostream>

struct A {
  template <int i>
  void dosomething();

  void show();
};

template<>
void A::dosomething<0>()
{
  std::cout << "0 in A" << std::endl;
}

void A::show()
{
  dosomething<0>();
}

struct B : A {
  // Without the following declaration:
  // error: template-id ‘dosomething<1>’ for ‘void B::dosomething()’
  // does not match any template declaration
  template <int i>
  void dosomething();
};

template<>
void B::dosomething<1>()
{
  std::cout << "1 in B" << std::endl;
}

int main()
{
  A x;
  x.dosomething<0>();

  B y;
  y.dosomething<0>(); // Link error!
  y.show();
  y.dosomething<1>();

  return 0;
}

The template member A::dosomething() is explicitly specialized for i=0 in the base class. The code for the template is explicitly generated, and called in the member A::show().

The first problem I found are:

A) Without a duplicated declaration

template <int i>
void dosomething();

inside the definition of B, the code does not compile, with the error:

template-id ‘dosomething<1>’ for ‘void B::dosomething()’
does not match any template declaration.

Why is the previous declaration in the base class A not visible?

B) The code above gives rise to a link error:

undefined reference to `void B::dosomething<0>()'

The error is due to the call y.dosomething<0>() in main. It can be avoided by calling instead y.A::dosomething<0>(). Why is dosomething<0>() apparently invisible in an instance of B?

francesco
  • 7,189
  • 7
  • 22
  • 49

1 Answers1

4

When you do out-of-line definition of a member function the declaration of that function is looked up in the class that is referred before the :: operator.

Consider this:

struct C { void test(); };
struct D : C { };

void D::test() { }; // error, test is not a member of D but of C

this is the same of doing

template<> void B::dosomething<1>() 
{ }

dosomething and all it specialization definitions must be qualified by the class it was declared on, i.e in A as the way you did with dosomething<0>.

Also notice that the declaration of dosomething in B is totally unrelated to that of A. you're getting a link error because of the call to a non defined specialization B::dosomething<0>.

You can create the specialization template<> void A::dosomething<1>(){ } but you're not getting the polymorphic behavior that you're expecting, A::dosomething<1> will be shared by all the derived class, if you really need differents versions of dosomething<1> by subclasses, you are confined to the initial repetition, and in order to access A::dosomething<0> from B you do it as static_cast<A&>(b).dosomething<0>().

You also should take a look at static polymorphism in this answer

Jans
  • 11,064
  • 3
  • 37
  • 45
  • Thanks for your answer. But I am still confused by the fact that, apparently, ```B``` does not see the template declaration of ```A```, and so I cannot further specialized the template function ```A::dosomething``` – francesco Nov 06 '18 at 20:37
  • @francesco I edited the answer to provide more clarification, let me know if there's something else that need clarification – Jans Nov 06 '18 at 21:10
  • Thanks a lot for the clarification and link. – francesco Nov 06 '18 at 21:40