C++带有模板的共享库:未定义符号错误

47

我正在尝试链接一个包含模板类的共享库,但是它给我返回“未定义符号”的错误。我已经将问题简化为大约20行代码。

shared.h

template <class Type> class myclass {
  Type x;
public:
  myclass() { x=0; }
  void setx(Type y);
  Type  getx();
};

shared.cpp

#include "shared.h"
template <class Type> void myclass<Type>::setx(Type y) { x = y; }
template <class Type> Type myclass<Type>::getx() { return x; }

main.cpp

#include <iostream>
#include "shared.h"
using namespace std;

int main(int argc, char *argv[]) {
   myclass<int> m;
   cout << m.getx() << endl;
   m.setx(10);
   cout << m.getx() << endl;
   return 0;
}

这是我编译库的方法:

g++ -fPIC -c shared.cpp -o shared.o
g++ -dynamiclib -Wl,-dylib_install_name -Wl,libshared.dylib -o libshared.dylib shared.o

主程序:

g++ -c main.cpp
g++ -o main  main.o -L. -lshared

仅获取以下错误:

Undefined symbols:
"myclass<int>::getx()", referenced from:
  _main in main.o
  _main in main.o
"myclass<int>::setx(int)", referenced from:
  _main in main.o

如果我在shared.h/cpp中移除'template'部分,并将它们替换为'int',一切都能正常工作。另外,如果我仅仅将模板类代码复制粘贴到main.cpp中,而不链接共享库,同样也可以正常工作。

我该如何让这样的模板类通过共享库工作?

我正在使用MacOS 10.5和GCC 4.0.1。


复制:https://dev59.com/snNA5IYBdhLWcg3wWsUA#999383 - Todd Gardner
4个回答

51

除了其他答案以外,你还可以显式实例化模板类。这只有在预先知道模板参数可能采用的类型时才有用。你可以在库中使用所有这些类型来实例化模板。

为了使你的示例编译通过,只需将以下内容添加到 shared.cpp 的末尾:

// Instantiate myclass for the supported template type parameters
template class myclass<int>;
template class myclass<long>;

这将使用Type=int来实例化模板,并将实例化后的代码放置在共享库中。根据需要添加尽可能多的显式实例化,以满足所需的所有类型要求。

再次强调,如果您想能够使用任意参数Type来实例化模板,则必须将定义添加到头文件中,以便编译器在其他编译单元中实例化时知道模板的源代码。


3
这正是我一直在寻找的。非常感谢!供其他遇到同样问题的人参考:将模板实例化语句添加到shared.cpp文件末尾。 - nolk
1
太棒了,但是如果我有“两个源文件”,而不是像OP一样只有一个呢?那么我应该在哪里添加这些行呢? - ForceBru
@ForceBru:两者都可以。我建议选择与定义模板的头文件相对应的源文件。 - Cookie
大规模的catcha问题已经解决 - 在CPP的模板命名空间中使用“template class myclass<type>”来修复DLL/SO中缺失的符号。编译器无法在对象中包含符号 - 对模板的工作原理不清楚。谢谢! - boardkeystown

20

模板函数定义必须位于头文件中。请将 shared.cpp 中的定义移至 shared.h。

因此,您不能将其编译为共享库,然后链接到它。这样是行不通的。


2
所以无法使用模板创建共享库吗? - nolk
2
相反,这非常简单。只需将其放在头文件中并共享即可。然后甚至不需要编译器;) 模板直到被实例化才会被编译,因此,如果您将模板放入.cpp文件中并将其编译为共享库,则模板代码会被简单地删除。模板定义必须对用户可见。 - jalf
你确定吗?因为我正在使用一个在编译时实例化模板的共享库。用户看不到代码,但可以使用它。当你混合需要生成的特殊化和已经生成的其他内容时,可能会遇到编译问题。但除此之外,它运行良好。 已通过g++和msvc测试。 - Michael
1
规则的例外:在.cpp文件中定义模板函数是可能的——只要该.cpp文件实例化了该模板即可。 - Craig M. Brandenburg

6

您需要在头文件中包含模板类的实现,这是C++模板的限制。因此,要么从主文件包含shared.cpp(#include ),要么将shared.cpp中的代码移动到shared.h中。


2

编译器必须查看模板的所有代码,以便为您想要使用的实际类型生成适当的代码。因此,您应该将所有代码放在.h文件中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接