Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it valid C++ to implement a template class's methods differently for different types without using specialization syntax?

Tags:

c++

I'm code reviewing a colleagues code and found this:

Header file:

template<class T>
class MyClass
{
  void Execute();
}

Cpp file:

void MyClass<int>::Execute()
{
  // something
}

void MyClass<string>::Execute()
{
  // something else
}

The code is specializing the function, but without using template specialization syntax. I guess it's working ok, but is it valid?

like image 427
Scott Langham Avatar asked Aug 23 '12 14:08

Scott Langham


2 Answers

Yes, it's perfectly valid to specialize methods of a template class.

But your syntax is wrong, it should be: (sorry, didn't see you were missing the template<> initially. Just assumed it was there and thought you were asking about member function specialization.)

template<>
void MyClass<int>::Execute()
{
  // something
}
template<>
void MyClass<string>::Execute()
{
  // something else
}

You need only declare these in the header. If you implement them in the header as well, you'll need to mark them inline to prevent multiple definition.

When calling a method, the version that suits the call most is called. Otherwise, the default.

In your case, if you specialize the template with a class X and attempt to call Execute, you'll get a linker error because you haven't provided a default implementation, nor a specialization for Execute for X.

like image 199
Luchian Grigore Avatar answered Nov 02 '22 06:11

Luchian Grigore


The question has already been answered, but let me draw attention to subtle differences between three cases.

Case 1: Specialization

header:

template <typename T> struct Foo
{
    void f() { /* stuff */ }
};

template <> void Foo<int>::f();

source:

template <> void Foo<int>::f() { /* ... */ }

In this case, Foo<T>::f() can be called for any T. The definition for the general case is auto-generated from the template; the definition for Foo<int>::f() is the one provided. Having the specialization in the header alerts every consuming translation unit that a separate symbol is to be looked up, rather than to use the template.


Case 2: Definition

header:

template <typename T> struct Foo
{
    void f();
};

source:

template <> void Foo<int>::f() { /* ... */ }

In this case, only Foo<int>::f() can be used; everything else will cause a linker error. Since there is no definition of the function in the template, every use of the template will cause a new symbol to be emitted, and only the one for Foo<int>::f() is provided by the shown translation unit.


Case 3: Flagrant error

header:

template <typename T> struct Foo
{
    void f() { /* stuff */ }
};

source:

template <> void Foo<int>::f() { /* ... */ }

This is a violation of the one-definition rule, since there are now multiple definitions of Foo<int>::f().

like image 26
Kerrek SB Avatar answered Nov 02 '22 04:11

Kerrek SB