【问题标题】:Linkage of explicit class template instantiation显式类模板实例化的链接
【发布时间】:2015-08-11 21:47:27
【问题描述】:

是否允许在不同的编译单元中使用相同类型的同一类模板的多个实例化?函数模板呢?

示例代码如下:

test.hpp

template <typename T>
class A
{
    public:
        T out();
};

template <typename T>
T A<T>::out()
{
    return T(1);
}

test1.cpp

#include "test.hpp"
template class A<int>;
int testFn()
{
    return A<int>().out();
}

test2.cpp

#include "test.hpp"
template class A<int>;
extern int testFn();
int main()
{
    return testFn() == A<int>().out();
}

如果我跑步

g++ -std=c++11 test1.cpp test2.cpp -o test

它编译时不会抱怨重复的定义。

我指的是标准 [1][2] 的旧草案,并假设链接部分没有太大变化(匿名命名空间除外)。类模板有 3.5p4 和 14p4 的外部链接。如果是这样,我希望 g++ 应该抱怨 A::out() 的重复定义。我在这里遗漏了什么吗?

如果 test.hpp 定义了一个没有“静态”或“内联”的函数模板呢?

谢谢。

[1]http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf [2]http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

【问题讨论】:

  • [temp.spec]p5 "对于一个给定的模板和一组给定的模板参数,一个明确的实例化定义在一个程序中最多出现一次 [...]诊断违反此规则的情况不需要实施。”
  • 另见CWG NAD 1045,其中包含可能未诊断出来的原因。
  • @dyp,在这种情况下,我的程序实际上违反了 14.7.0.5,但标准说 g++ 不需要诊断这一点,实际上 g++ 解决了多重定义。但是为了使其符合标准,我应该只在一个翻译单元中定义所有实例化,并在其他翻译单元中声明(“extern”)显式模板实例化。我说的对吗?
  • 我猜是这样,虽然我不太明白这个要求的目的 a) 类模板特化 b) 函数模板特化是 inline 或具有内部链接。
  • @dyp,这让我大吃一惊。基本上我写的任何 C++ 代码都是错误的。我应该一直添加extern template class std::vector&lt;int&gt;。

标签: c++ templates c++11 linkage


【解决方案1】:

在实现中找到这些问题的答案的一个好方法是使用“nm”。如果将 nm 的输出通过管道传输到 c++filt,则损坏的 C++ 符号通常更具可读性。

例如,如果您使用“-c”编译每个编译单元的“.o”,那么您可以运行 nm。当我这样做时,我看到模板成员是弱符号“W”代码(在 x86 linux 上)。这意味着多个定义是可以的,并且以某些系统特定的方式。如果我创建了一个非模板化的函数,它将在两个翻译单元对应的目标文件中显示为“T”。这将导致多个定义的符号。

通常 C++ 模板会根据需要进行实例化(无需完全实例化),这将允许您在声明标头之外使用 _impl 类型标头。

实际上不允许您将成员模板定义为静态的(这会产生错误)。您可以将其定义为内联。在这种情况下,您将看不到使用 nm 的成员模板的任何符号,因为它已被内联。

【讨论】:

  • 在您的回答中,您提到了“以某种系统特定的方式”。这是否意味着它没有被标准指定并且可能不可移植?至于我关于函数模板的最后一个问题,我的意思是更通用的函数模板,而不是类成员函数模板。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-09
  • 2021-11-25
  • 1970-01-01
  • 1970-01-01
  • 2021-05-02
  • 1970-01-01
相关资源
最近更新 更多