Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template specialization - different behaviour between MSVC and GCC/MinGW

I specialized a template and encountered a different beahviour between the MSVC compiler and MinGW/GCC. Here is the header file:

// MyClass.h
#ifndef MYCLASS_HEADER
#define MYCLASS_HEADER

template<typename T>
class MyClass {
public:
   virtual void doSomething() {
      // some code
   }
};

// specialization prototype here

#endif

Now, the difference. To avoid multiple definitions, i specialized only a prototype in the header file, the implementation is in a cpp file. The specialization is the problem. the GCC/MinGW compiler accepts only this one:

template<> void MyClass<int>::doSomething(); // GCC version

And MSVC only this:

extern template void MyClass<int>::doSomething(); // MSVC version

The implementation in the CPP file is the same for both:

// MyClass.cpp
#include "MyClass.h"

template<> void MyClass<int>::doSomething() {
   // some code
}

with the GCC prototype the MSVC compiler throws an "unresolved external symbol" error, GCC with the MSVC version throws "specialization of ... after instantiation".

At the moment, i have the workaround:

#ifdef _MSC_VER
extern template
#else
template<>
#endif
void MyClass<int>::doSomething();

in the header file. It works, but i don't like it. Is there a way to avoid the compiler specific switch?

like image 454
WoJo Avatar asked Jun 19 '13 15:06

WoJo


1 Answers

These are not the same thing:

template<> void MyClass<int>::doSomething(); // GCC version

extern template void MyClass<int>::doSomething(); // MSVC version

The first one declares an explicit specialization, the second one declares an explicit instantiation.

with the GCC prototype the MSVC compiler throws an "unresolved external symbol" error, GCC with the MSVC version throws "specialization of ... after instantiation".

The GCC error is telling you that there has been an implicit instantiation of MyClass<int>::doSomething() earlier in the file than the declaration of the explicit specialization; that is a bug in your code. You must declare the explicit specialization before any use of it that causes an implicit instantiation, otherwise that implicit instantiation will use the primary template definition, not the specialization. You can usually ensure that the specialization is declared before it's used by declaring it directly after the definition of the primary template, before any instantiations of MyClass<int>.

I don't know why you get the MSVC error, it sounds as though you are failing to link to MyClass.cpp. If that's not it, you could try adding an explicit instantiation definition to MyClass.cpp to force the symbol to be emitted in that file:

template void MyClass<int>::doSomething();  // N.B. no "extern" on definition

Have you tried declaring both the specialization and instantiation, but ensuring they are declared before any implicit instantiations? That should be valid C++ e.g.

#ifndef MYCLASS_H
#define MYCLASS_H
template<typename T>
class MyClass {
public:
   virtual void doSomething() {
      // some code
   }
};
// declare explicit specialization
template<> void MyClass<int>::doSomething();
// declare explicit instantiation
extern template void MyClass<int>::doSomething();
#endif

.

// MyClass.cpp
#include "MyClass.h"

// define explicit specialization
template<> void MyClass<int>::doSomething() {
   // some code
}
// define explicit instantiation
template void MyClass<int>::doSomething();

Alternatively, if you can't make that work with both compilers, you could put the definition of the specialized member function in the header and declare it inline, which would also avoid multiple definition errors.

like image 194
Jonathan Wakely Avatar answered Oct 25 '22 10:10

Jonathan Wakely