3

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

规格.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();
}

规范.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 和直接使用目标文件有什么区别?:-)

谢谢!

4

3 回答 3

8

从第 14.7.3p6 节开始:

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

您的程序格式错误,因为您在 spec.cpp 中使用了专门化,而没有在该翻译单元中首先声明它。或者,如以下段落所述:

函数模板、类模板、类模板的成员函数、类模板的静态数据成员、类模板的成员类、类模板的成员枚举、类模板的成员类模板、类的成员函数模板的显式特化声明的放置模板,类模板的成员模板的成员函数,非模板类的成员模板的成员函数,类模板的成员类的成员函数模板等,以及类模板的部分特化声明的放置,的成员类模板非模板类、类模板的成员类模板等,根据显式特化声明的相对定位及其在翻译单元中的实例化点(如上文和下文指定),可能会影响程序是否格式正确。

在编写专业时
要注意它的位置;
或者让它编译
将是一种试炼
,以点燃它的自焚。

我投票认为它是整个 Standard 中最棒的打油诗段落

于 2012-10-01T21:55:37.310 回答
2

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

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

于 2012-10-01T21:59:52.617 回答
1

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

于 2012-10-01T21:56:50.623 回答