c++ - 关于 C++ 模板和显式声明

标签 c++ c++11

我只花了大约 20 分钟试图弄清楚为什么我的一些模板方法通过了编译但没有链接。

原来我需要显式声明我的模板方法。

是这样的:

class Test {
   template<class Source> void Save(Source& obj);
};

然后我会在某个地方像这样使用它:

Test t;
ClassDerivedFromInterface obj;
t.Save(obj);

它编译正常但没有链接。直到我添加:

template void Test::Save(ClassDerivedFromInterface);

我想了解在什么情况下需要明确声明。

谢谢

最佳答案

简而言之,您需要让模板函数的整个主体(定义)对实例化模板的翻译单元可见。因此,当您说 t.Save(obj); 时,该翻译单元应该可以访问 Save 的定义。通常,您通过在头文件本身中包含函数模板的定义来实现这一点。

这样做的原因是模板不是普通的代码,可以编译后可以随意链接。相反,模板是一种代码生成工具,可以根据需要生成必要的代码 - 复制/粘贴的自动版本,然后是搜索和替换,如果您愿意的话。

因此,您的函数 Save(ClassDerivedFromInterface&) 的实际可编译代码在您编写该行之前不会存在。如果只有函数模板的声明是可见的,那么模板只生成具体函数的声明,而不是函数体,因此在链接时您会注意到函数丢失了。

总而言之,模板本身是不能被编译的,只有它们的具体实例可以,而且你在实例化它们时必须注意确保具体实例始终可用。您拥有的显式实例化可以工作,并允许您将一些特定实例打包到一个单独的 TU 中,但通常这很难维护且不可扩展,并且当您让编译器隐式实例化时,您可以避免显式实例化的其他缺点。所以通常最好将您的整个定义打包到头文件中。

关于c++ - 关于 C++ 模板和显式声明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7134617/

相关文章:

c++ - 在 C++ 中推迟静态变量构造

c++ - 如何使用 C++ 关闭所有子线程

c++ - vector 、迭代器和 std::find

c++ - 使用从外部参数包中获取的参数类型声明一个函数

c++ - 我正在使用 C++ 读写文件

c++ - 当类型参数为空时无法实例化函数模板

c++ - 为什么 is_default_constructible<Class>::value 在同一类范围内失败

c++ - 这个用户定义的转换没有歧义吗?如果可以,什么规则允许这样做?

c++11 - C++ : For which objects, 是 "moved"意味着超过 "staying valid"?

c++ - 为什么 std::map 有一个 find 成员函数?