【问题标题】:Visibility of template specialization of C++ functionC++ 函数模板特化的可见性
【发布时间】:2010-09-08 17:29:56
【问题描述】:
假设我有fileA.h,它声明了一个带有模板函数SomeFunc<T>() 的类classA。该函数直接在头文件中实现(与模板函数一样)。现在我在fileA.C(即不在头文件中)中添加了SomeFunc()(如SomeFunc<int>())的专门实现。
如果我现在从其他代码(可能也来自另一个库)调用SomeFunc<int>(),它会调用通用版本还是专业化版本?
我现在有这个问题,类和函数存在于一个库中,由两个应用程序使用。一个应用程序正确地使用了专业化,而另一个应用程序使用了通用形式(这会导致稍后出现运行时问题)。为什么有区别?这可能与链接器选项等有关吗?这是在 Linux 上,使用 g++ 4.1.2。
【问题讨论】:
标签:
c++
linux
templates
linker
g++
【解决方案1】:
对在调用点不可见的模板进行专门化是错误。不幸的是,编译器不需要诊断这个错误,然后可以对你的代码做他们喜欢的事情(在标准中它是“格式错误,不需要诊断”)。
从技术上讲,您需要在头文件中定义特化,但几乎每个编译器都会按照您的预期处理这个问题:这在 C++11 中通过新的“外部模板”工具修复:
extern template<> SomeFunc<int>();
这明确地声明了特定的专业化是在别处定义的。许多编译器已经支持这一点,有些带有extern,有些没有。
【解决方案2】:
您是否在头文件中添加了带有参数的原型?
我的意思是在 fileA.h 中的某个地方
template<> SomeFunc<int>();
如果不是这可能就是原因。
【解决方案3】:
我在 gcc4 上遇到了同样的问题,这就是我解决它的方法。这是一个比我之前的 cmets 所相信的更简单的解决方案。以前的帖子想法是正确的,但它们的语法对我不起作用。
----------header-----------------
template < class A >
void foobar(A& object)
{
std::cout << object;
}
template <>
void foobar(int);
---------source------------------
#include "header.hpp"
template <>
void foobar(int x)
{
std::cout << "an int";
}
【解决方案4】:
根据规范,您的专用函数模板永远不应在 fileA.C 之外调用,除非您 export 模板定义,目前没有编译器(Comeau 除外)支持(或计划在可预见的未来)。
另一方面,一旦函数模板被实例化,编译器就会看到一个不再是模板的函数。 GCC 可以在不同的编译器单元中重复使用这个定义,因为标准规定每个模板只能针对给定的一组类型参数 [temp.spec] 实例化一次。不过,由于没有导出模板,这应该仅限于编译单元。
我相信 GCC 在跨编译单元共享其实例化模板列表时可能会暴露一个错误。通常,这是一个合理的优化,但它应该考虑到它似乎没有正确执行的函数特化。
【解决方案5】:
在 Microsoft C++ 中,我用内联函数做了一个实验。我想知道如果我在不同的来源中定义了不兼容的函数版本会发生什么。根据我使用的是 Debug 构建还是 Release 构建,我得到了不同的结果。在 Debug 中,编译器拒绝内联任何内容,并且链接器正在链接相同版本的函数,无论源代码中的范围是什么。在 Release 中,编译器会内联当时已定义的任何版本,并且您会得到不同版本的函数。
在这两种情况下都没有任何警告。我有点怀疑这一点,这就是我做这个实验的原因。
我假设模板函数的行为与其他编译器一样。
【解决方案6】:
正如 Anthony Williams 所说,extern template 构造是执行此操作的正确方法,但由于他的示例代码不完整且存在多个语法错误,因此这里有一个完整的解决方案。
文件A.h:
namespace myNamespace {
class classA {
public:
template <class T> void SomeFunc() { ... }
};
// The following line declares the specialization SomeFunc<int>().
template <> void classA::SomeFunc<int>();
// The following line externalizes the instantiation of the previously
// declared specialization SomeFunc<int>(). If the preceding line is omitted,
// the following line PREVENTS the specialization of SomeFunc<int>();
// SomeFunc<int>() will not be usable unless it is manually instantiated
// separately). When the preceding line is included, all the compilers I
// tested this on, including gcc, behave exactly the same (throwing a link
// error if the specialization of SomeFunc<int>() is not instantiated
// separately), regardless of whether or not the following line is included;
// however, my understanding is that nothing in the standard requires that
// behavior if the following line is NOT included.
extern template void classA::SomeFunc<int>();
}
文件A.C:
#include "fileA.h"
template <> void myNamespace::classA::SomeFunc<int>() { ... }
【解决方案7】:
Brandon:我就是这么想的——永远不应该调用专门的函数。我提到的第二个应用程序也是如此。然而,第一个应用程序清楚地调用了专门化形式,即使在头文件中没有声明专门化!
我这里主要是求启发:-)因为第一个app是单元测试,不幸的是有一个bug没有出现在测试中,而是出现在真实的app中......
(PS:我已经修复了这个特定的错误,实际上是通过在标题中声明专业化;但是还有哪些类似的错误可能仍然隐藏?)
【解决方案8】:
@[安东尼-威廉姆斯],
您确定没有将extern 模板声明与extern template 实例化混淆吗?据我所见,extern template 可能仅用于显式实例化,而不用于专门化(这意味着隐式实例化)。 [temp.expl.spec] 没有提到extern 关键字:
显式专业化:
template 声明
【解决方案9】:
除非头文件中还列出了专用模板功能,否则其他应用程序将不知道专用版本。解决方案是将SomeFunc<int>()也添加到标题中。