模板特化 - MSVC和GCC / MinGW之间的不同行为

时间:2013-06-19 15:21:31

标签: c++ templates visual-c++ gcc mingw

我专门设计了一个模板,在MSVC编译器和MinGW / GCC之间遇到了不同的问题。这是头文件:

// MyClass.h
#ifndef MYCLASS_HEADER
#define MYCLASS_HEADER

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

// specialization prototype here

#endif

现在,差异。为了避免多个定义,我只在头文件中专门设计原型,实现在cpp文件中。专业化是问题所在。 GCC / MinGW编译器只接受这一个:

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

MSVC只有这个:

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

CPP文件中的实现对于两者都是相同的:

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

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

使用GCC原型,MSVC编译器抛出“未解析的外部符号”错误,GCC与MSVC版本抛出“实例化后......的专业化”。

目前,我有解决方法:

#ifdef _MSC_VER
extern template
#else
template<>
#endif
void MyClass<int>::doSomething();
头文件中的

。它有效,但我不喜欢它。有没有办法避免编译器特定的开关?

2 个答案:

答案 0 :(得分:5)

这些不是一回事:

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

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

第一个声明了一个显式的特化,第二个声明了一个显式的实例化

  

使用GCC原型,MSVC编译器抛出“未解析的外部符号”错误,GCC与MSVC版本抛出“实例化后......的专业化”。

GCC错误告诉您文件中先前有MyClass<int>::doSomething()的隐式实例化,而不是显式特化的声明;这是代码中的错误。您必须在使用它之前声明显式特化,这会导致隐式实例化,否则隐式实例化将使用主模板定义,而不是特化。在MyClass<int>的任何实例化之前,您可以通过在主模板定义之后直接声明专门化来确保在专业化使用之前声明专业化。

我不知道你为什么会收到MSVC错误,听起来好像你没有链接到MyClass.cpp。如果不是这样,您可以尝试向MyClass.cpp添加显式实例化定义,以强制在该文件中发出符号:

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

您是否曾尝试声明两者专业化和实例化,但确保在任何隐式实例化之前声明?这应该是有效的C ++,例如

#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();

或者,如果你不能使用这两个编译器,你可以将专用成员函数的定义放在头文件中并声明它inline,这也可以避免多个定义错误。

答案 1 :(得分:0)

看起来标准说extern是正确的,在某些实现中可能是一个错误:

摘录自14.7.2显式实例化:

“显式实例化的语法是:
extern opt 模板 声明

有两种形式的显式实例化:显式实例化定义和显式实例化声明。显式实例化声明以extern关键字开头。“