链接到ar后,专用成员函数丢失

时间:2012-10-01 21:49:28

标签: c++ templates linker ar

考虑以下最小的例子,它在一个更大的项目中重现了一个问题:

spec.h:

#include <iostream>

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

other.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和直接对象文件的使用有什么区别? : - )

谢谢!

3 个答案:

答案 0 :(得分:8)

从第14.7.3p6节:

  

如果模板,成员模板或类模板的成员明确专门化,那么应该在第一次使用该特化之前声明该特化,这将导致在每个翻译单元中发生隐式实例化发生这种用途;无需诊断。如果程序没有为显式特化提供定义,并且特殊化的使用方式会导致隐式实例化或成员是虚拟成员函数,则程序格式错误,无需诊断。

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

  

为函数模板,类模板,类模板的成员函数,类模板的静态数据成员,类模板的成员类,类模板的成员枚举,类模板的成员类模板,成员函数放置显式特化声明类模板的模板,类模板的成员模板的成员函数,非模板类的成员模板的成员函数,类模板的成员类的成员函数模板等,以及类模板的部分特化声明的放置,成员非模板类的类模板,类模板的成员类模板等,可以根据显式专业化声明的相对位置及其在翻译单元中的实例化点的相对位置来影响程序是否格式正确,如上所述,下方。

     

在撰写专业时,请注意其位置;或者使其编纂,将是这样的试验,以点燃其自焚。

我在整个标准中投票选为最棒的 limerick。

答案 1 :(得分:2)

Ben Voigt's answer是正确的,但我想补充一点。

你基本上得到了两个不同版本的函数,一个在other.o中,另一个在spec.o中(由内联模板生成)。链接器旨在选择一个且仅选择一个,假设它们与标准要求相同。在第一种情况下,如果尚未定义符号,链接器将仅从库中提取定义。由于它是在spec.o中定义的,因此不使用库定义。

答案 2 :(得分:1)

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