通过ar链接后失去了专门的成员功能

specialized member function lost after linking thru ar

本文关键字:成员 功能 ar 链接 失去 通过      更新时间:2023-10-16

考虑以下在更大的项目中重现问题的最小示例:

规范h:

#include <iostream>
class A
{
public:
template<typename T>
T test(const std::string& a)
{
std::cout << "DEFAULT CALLED WITH " << a << "n";
return T();
}
};

其他.cpp:

#include "spec.h"
template<>
float A::test<float>(const std::string& a)
{
std::cout << "SPECIAL CALLED WITH " << a << "n";
return float();
}

spec.cpp:

#include <iostream>
#include "spec.h"
int main()
{
A a;
a.test<int>("int");
a.test<float>("float");
return 0;
}

编译:

$ make
rm -f *.o lib.a output
clang++ -g other.cpp -c
clang++ -g spec.cpp -c
ar cr lib.a other.o
clang++ -g -o output lib.a spec.o
rm -f *.o output2
clang++ -g other.cpp -c
clang++ -g spec.cpp -c
clang++ -g -o output2 other.o spec.o
$ ./output
DEFAULT CALLED WITH int
DEFAULT CALLED WITH float
$ ./output2
DEFAULT CALLED WITH int
SPECIAL CALLED WITH float

问题:

为什么会发生这种情况?它不知怎么被剥光了吗?lib.a和直接使用对象文件之间有什么区别?:-)

谢谢!

来自第14.7.3p6:节

如果模板、成员模板或类模板的成员是显式专门化的,则应在首次使用该专门化之前声明该专门化,这将导致在发生此类使用的每个翻译单元中发生隐式实例化;不需要进行诊断。如果程序没有为显式专门化提供定义,并且专门化的使用方式会导致隐式实例化,或者成员是虚拟成员函数,则程序格式错误,不需要诊断。

您的程序格式不正确,因为您在spec.cpp中使用了专门化,而没有在该翻译单元中首先声明它。或者,正如下面一段所说:

函数模板的显式专用化声明、类模板、类模板的成员函数、类模板静态数据成员、类模板成员类、类模板委员枚举、类模板会员类模板、,类模板的成员模板的成员函数、非模板类的成员模板成员函数、类模板成员类的成员函数模板等,以及类模板的部分专用化声明、非模板类别的成员类别模板、类模板的会员类别模板等的放置。,根据显式专门化声明的相对位置及其在翻译单元中的实例化点,可以影响程序是否形成良好。

编写专业化
时要注意其位置
或使其编译
将是一场考验
,从而点燃其自焚

我投票认为是整个标准中最令人敬畏的段落打油诗

Ben Voigt的答案是正确的,但我想补充一点。

您实际上得到了该函数的两个不同版本,一个在other.o中,另一个在spec.o中(由内联模板生成)。链接器被设计为选择一个并且只选择一个,假设它们都与标准要求的相同。在第一种情况下,只有在符号尚未定义的情况下,链接器才会从库中提取定义。由于它是在spec.o中定义的,所以没有使用库定义。

通过标题中的定义,每个翻译单元都可以创建自己的实例化。因此,永远不会有一个未定义的符号引用您的专用版本。相应地,在查看库时,不包括具有专用版本的对象文件:它没有定义任何未定义的符号。当链接时显式包含对象文件时,链接器除了包含它之外别无选择。然而,您需要声明所有专门化:如果没有声明,编译器就不会知道通用版本不适用。因此,这个版本会发生什么,无论是否使用,都取决于符号的处理方式。