使用 clang 显式 C++ 模板实例化

car*_*ama 5 c++ linker templates explicit-instantiation

注意:几个相关问题(例如,这个问题)最终被标记为该问题的重复项。我知道这个特定问题并遵循相应答案中的解决方案。然而,不同的编译器会产生不同的行为,我不知道为什么。

我的库具有一个类模板,我想为库中的某些模板参数提供实例,因为该模板需要一些重要的编译时间。类模板可能如下所示 ( stack.hpp)

#ifndef MY_STACK
#define MY_STACK

template<class T>
class stack
{
public:
    stack();
};

#endif
Run Code Online (Sandbox Code Playgroud)

其实现驻留在相应的stack.tpp文件中

#ifndef MY_STACK_TPP
#define MY_STACK_TPP

#include <iostream>

template<class T>
stack<T>::stack()
{
    std::cout << "My stack constructor!" << std::endl;
}

#endif
Run Code Online (Sandbox Code Playgroud)

由于我只想提供对某些模板参数的支持,因此我stack.cpp创建了以下显式模板实例:

#include "stack.hpp"

template class stack<double>;
template class stack<char>;

#include "stack.tpp"
Run Code Online (Sandbox Code Playgroud)

它使用 g++ 和 clang++ 进行编译,但生成的共享库的符号存在差异:

g++ -std=c++11 -c stack.cpp -o stack.so
nm -C stack.so | grep stack
0000000000000049 t _GLOBAL__sub_I_stack.cpp
0000000000000000 W stack<char>::stack()
0000000000000000 W stack<char>::stack()
0000000000000000 n stack<char>::stack()
0000000000000000 W stack<double>::stack()
0000000000000000 W stack<double>::stack()
0000000000000000 n stack<double>::stack()
Run Code Online (Sandbox Code Playgroud)

clang++-7 -std=c++11 -c stack.cpp -o stack.so
nm -C stack.so | grep stack
0000000000000050 t _GLOBAL__sub_I_stack.cpp
Run Code Online (Sandbox Code Playgroud)

在我的应用程序中,使用 clang++ 找不到此类显式实例化类的构造函数,但它与 g++ 一起工作正常。我想这个基本的 MWE 给出了原因。谁能告诉我如何使用 clang++ 获取类模板的构造函数符号?

dfr*_*fri 6

这个程序格式良好

引用[temp.explicit]/1 [强调我的]:

类、函数、变量或成员模板特化可以从其模板显式实例化。类模板的成员函数、成员类或静态数据成员可以从与其类模板关联的成员定义中显式实例化。[..]

并且,引用[temp.explicit]/9 [强调我的]:

命名类模板特化的显式实例化定义显式地实例化类模板特化,并且是仅那些在实例化点已定义的成员的显式实例化定义

Thus, in the OPs example, the explicit instantiation definition of stack<T> will not include an explicit instantiation definition of the constructor, as the explicit instantiation definition of stack<T> is placed before providing the definition of it constructor via the .tpp include.

Quoting [temp.point]/8 [emphasis mine]:

A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.

因此,对于 sstack.cpp包含两个不同的实例化点,并且一个在包含 之前,一个在包含之后的情况stack.tpp,则该程序是格式错误的。

然而,这里变得有点棘手,因为实例化点取决于类模板及其成员函数(/构造函数)的使用方式。正如上面引用的[temp.explicit]/9所涵盖的, 的显式实例化stack<T>不会导致其构造函数的显式实例化定义,我们将需要依靠[temp.point]来获取详细信息,特别是第 1 条, 2 和 4,关于何时其使用上下文将导致包含 之前的实例化点stack.tpp

问题中的独立示例不属于任何这些情况,因此该程序不是格式错误的。

GCC 与 clang:看似不同的实例化行为?

谁能告诉我如何使用 clang++ 获取类模板的构造函数符号?

由于构造函数从未被使用过,所以它根本不应该(需要)被实例化,但似乎(从 OP 符号转储来看)GCC 无论如何都会这样做(这不是非法的),而 clang 则不然。如果在包含使用/引用构造函数track.tpp,那么 GCC 和 clang 都会自然地实例化它(对于所使用的特定专业化),因为它们随后需要这样做。