模板特化导致在Windows上与MinGW链接错误而在Linux上不是GCC

时间:2013-07-18 10:05:07

标签: c++ linux windows gcc mingw

以下构建设置在使用GCC(4.6.3)的Linux上运行正常,但在使用GCC(4.7.2)的MinGW中运行不正常。


$ cat Makefile 
all:
        g++ -c foo.cpp
        g++ -c bar.cpp
        g++ bar.o foo.o -o bar

$ cat foo.h 
#ifndef FOO_H
#define FOO_H

#include <iostream>

template <typename T>
void foo(T x) {
    std::cout << "Hello World!" << std::endl;
}
#endif

$ cat foo.cpp
#include "foo.h"
template <>
void foo(int x) {
    std::cout << "Hello Int!" << std::endl;
}

$ cat bar.cpp 
#include "foo.h"

int main() {
    foo <int> (1);
}

在Linux上,我们有:

$ make
g++ -c foo.cpp
g++ -c bar.cpp
g++ bar.o foo.o -o bar

$ ./bar
Hello Int!

这是我的期望。在Windows上,我们有

$ make
g++ -c foo.cpp
g++ -c bar.cpp
g++ bar.o foo.o -o bar
foo.o:foo.cpp:(.text+0x0): multiple definition of `void foo<int>(int)'
bar.o:bar.cpp:(.text$_Z3fooIiEvT_[__Z3fooIiEvT_]+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
make: *** [all] Error 1

我怀疑这与弱符号有关。意思是,在Linux上我们有foo.o

00000000 T _Z3fooIiEvT_

并在bar.o

00000000 W _Z3fooIiEvT_

而在Windows上我们在foo.o

中有
00000000 T __Z3fooIiEvT_

并在bar.o

00000000 T __Z3fooIiEvT_

因此,没有弱的符号可以覆盖。

解决此问题的最佳方法是什么?在实际情况中,我有一个带有许多模板定义的头文件foo.h。其中一些,我专注,我把这些定义放在foo.cpp中,后来编译成一个库。然后,我将标题和库提供给用户。如果可能的话,我总是想在foo库中使用特化。如果不存在特化,我想在foo标题中使用模板定义。

修改

foo.h的以下修改似乎解决了问题

$ cat foo.h 
#ifndef FOO_H
#define FOO_H

#include <iostream>

template <typename T>
void foo(T x) {
    std::cout << "Hello World!" << std::endl;
}
template <>
void foo(int x);
#endif

基本上,foo的int版本的原型需要在头文件中。这符合BoBTFish的说明,该标准要求“专业化应在首次使用前申报”。无论如何,这是设置专业化库的最佳方法吗?

2 个答案:

答案 0 :(得分:2)

不知道编译器的复杂性,但你仍然违反了标准:

  

14.7.3明确的专业化:

     

6如果是模板,成员模板或类模板的成员   明确专业化,然后声明专业化   在第一次使用该特化之前会导致一个   隐式实例化发生在每个翻译单元中   发生了这种用途;无需诊断。

所以你的程序结构不合理。

答案 1 :(得分:1)

您可以安装(在Windows和Linux上)最近的GCC(今天是4.8.1)并使用C ++ 11的extern template功能。