Template specialization of a single method from a templated class

C++Visual Studio-2008TemplatesSpecialization

C++ Problem Overview


Always considering that the following header, containing my templated class, is included in at least two .CPP files, this code compiles correctly:

template <class T>
class TClass 
{
public:
  void doSomething(std::vector<T> * v);
};

template <class T>
void TClass<T>::doSomething(std::vector<T> * v) {
  // Do something with a vector of a generic T
}

template <>
inline void TClass<int>::doSomething(std::vector<int> * v) {
  // Do something with a vector of int's
}

But note the inline in the specialization method. It is required to avoid a linker error (in VS2008 is LNK2005) due to the method being defined more then once. I understand this because AFAIK a full template specialization is the same as a simple method definition.

So, how do I remove that inline? The code should not be duplicated in every use of it. I've searched Google, read some questions here in SO and tried many of the suggested solutions but none successfully built (at least not in VS 2008).

Thanks!

C++ Solutions


Solution 1 - C++

As with simple functions you can use declaration and implementation. Put in your header declaration:

template <>
void TClass<int>::doSomething(std::vector<int> * v);

and put implementation into one of your cpp-files:

template <>
void TClass<int>::doSomething(std::vector<int> * v) {
 // Do somtehing with a vector of int's
}

Don't forget to remove inline (I forgot and thought this solution will not work :) ). Checked on VC++2005

Solution 2 - C++

You need to move specialization definition to CPP file. Specialization of member function of template class is allowed even if function is not declared as template.

Solution 3 - C++

There is no reason to remove the keyword inline.
It does not change the meaning of the code in anyway.

Solution 4 - C++

If you want to remove the inline for whatever reason the solution of maxim1000 is perfectly valid.

In your comment, though, it seems you believe that the inline keyword means that the function with all his contents gets always inlined but AFAIK that is actually very much dependent on your compiler optimization.

Quoting from the C++ FAQ

> There are several ways to designate that a function is inline, some of > which involve the inline keyword, others do not. No matter how you > designate a function as inline, it is a request that the compiler is > allowed to ignore: the compiler might inline-expand some, all, or none > of the places where you call a function designated as inline. (Don’t > get discouraged if that seems hopelessly vague. The flexibility of the > above is actually a huge advantage: it lets the compiler treat large > functions differently from small ones, plus it lets the compiler > generate code that is easy to debug if you select the right compiler > options.)

So, unless you know that that function will actually bloat your executable or unless you want to remove it from the template definition header for other reasons, you can actually leave it where it is without any harm

Solution 5 - C++

This is a little OT, but I thought I'd leave this here in case it helps someone else. I was googling about template specialization which led me here, and while @maxim1000's answer is correct and ultimately helped me figure my problems out, I didn't think it was abundantly clear.

My situation is a little different (but similar enough to leave this answer I think) than the OP's. Basically, I'm using a third party library with all different kinds of classes that define "status types". The heart of these types are simply enums, but the classes all inherit from a common (abstract) parent and provide different utility functions, such as operator overloading and a static toString(enum type) function. Each status enum is different from one another and unrelated. For example, one enum has the fields NORMAL, DEGRADED, INOPERABLE, another has AVAILBLE, PENDING, MISSING, etc. My software is in charge of managing different types of statuses for different components. It came about that I wanted to utilize the toString functions for these enum classes, but since they're abstract I couldn't instantiate them directly. I could have extended each class I wanted to use, but ultimately I decided to create a template class, where the typename would be whatever concrete status enum I cared about. Probably some debate can be had about that decision, but I felt like that was a lot less work than extending each abstract enum class with a custom one of my own and implementing the abstract functions. And of course in my code, I just wanted to be able to call .toString(enum type) and have it print the string representation of that enum. Since all the enums were entirely unrelated, they each had their own toString functions that (after some research I learned) had to be called using template specialization. That led me here. Below is an MCVE of what I had to do in order to make this work correctly. And actually my solution was a bit different than @maxim1000's.

This is a (greatly simplified) header file for the enums. In reality, each enum class was defined in it's own file. This file represents the header files that are supplied to me as part of the library I am using:

// file enums.h
#include <string>

class Enum1
{
public:
  enum EnumerationItem
  {
    BEARS1,
    BEARS2,
    BEARS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

class Enum2
{
public:
  enum EnumerationItem
  {
    TIGERS1,
    TIGERS2,
    TIGERS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

adding this line just to separate the next file into a different code block:

// file TemplateExample.h
#include <string>

template <typename T>
class TemplateExample
{
public:
  TemplateExample(T t);
  virtual ~TemplateExample();

  // this is the function I was most concerned about. Unlike @maxim1000's
  // answer where (s)he declared it outside the class with full template
  // parameters, I was able to keep mine declared in the class just like
  // this
  std::string toString();

private:
  T type_;
};

template <typename T>
TemplateExample<T>::TemplateExample(T t)
  : type_(t)
{

}

template <typename T>
TemplateExample<T>::~TemplateExample()
{

}

next file

// file TemplateExample.cpp
#include <string>

#include "enums.h"
#include "TemplateExample.h"

// for each enum type, I specify a different toString method, and the
// correct one gets called when I call it on that type.
template <>
std::string TemplateExample<Enum1::EnumerationItem>::toString()
{
  return Enum1::toString(type_);
}

template <>
std::string TemplateExample<Enum2::EnumerationItem>::toString()
{
  return Enum2::toString(type_);
}

next file

// and finally, main.cpp
#include <iostream>
#include "TemplateExample.h"
#include "enums.h"

int main()
{
  TemplateExample<Enum1::EnumerationItem> t1(Enum1::EnumerationItem::BEARS1);
  TemplateExample<Enum2::EnumerationItem> t2(Enum2::EnumerationItem::TIGERS3);

  std::cout << t1.toString() << std::endl;
  std::cout << t2.toString() << std::endl;

  return 0;
}

and this outputs:

BEARS1
TIGERS3

No clue if this is the ideal solution to solve my problem, but it worked for me. Now, no matter how many enumeration types I end up using, all I have to do is add a few lines for the toString method in the .cpp file, and I can use the libraries already-defined toString method without implementing it myself and without extending each enum class I want to use.

Solution 6 - C++

I'd like to add that there is still a good reason to keep the inline keyword there if you intend to leave also the specialization in the header file.

> "Intuitively, when you fully specialize something, it doesn't depend on a template parameter any more -- so unless you make the specialization inline, you need to put it in a .cpp file instead of a .h or you end up violating the one definition rule..."

Reference: https://stackoverflow.com/a/4445772/1294184

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChuimView Question on Stackoverflow
Solution 1 - C++maxim1000View Answer on Stackoverflow
Solution 2 - C++BostonLoganView Answer on Stackoverflow
Solution 3 - C++Martin YorkView Answer on Stackoverflow
Solution 4 - C++TriskeldeianView Answer on Stackoverflow
Solution 5 - C++yanoView Answer on Stackoverflow
Solution 6 - C++JordanView Answer on Stackoverflow