跨TU的类模板重载

时间:2018-01-23 10:43:19

标签: c++ c++11 language-lawyer

考虑以下C ++ 11应用程序:

A.cpp:

template<typename T>
struct Shape {
    T x;
    T area() const { return x*x; }
};

int testA() {
    return Shape<int>{2}.area();
}

B.cpp:

template<typename T, typename U = T>
struct Shape {
    T x;
    U y;
    U area() const { return x*y; }
};
int testB() {
    return Shape<int,short>{3,4}.area();
}

Main.cpp的:

int testA();
int testB();
int main() {
   return testA() + testB();
}

虽然它编译(只要A和B在不同的TU中),它看起来不正确,而且我无法弄清楚原因。

因此我的问题:这是否违反了ODR,重载或任何其他规则,如果是,那么the Standard的哪些部分会被违反?为什么?

1 个答案:

答案 0 :(得分:13)

这是ODR违规行为。 Template names have linkage。这两个模板名称都有外部链接,[basic.link]/4表示:

  

直接或间接声明的未命名命名空间或命名空间   在未命名的命名空间内有内部链接。所有其他名称空间   有外部联系。具有未命名的命名空间范围的名称   上面给出的内部连接具有与封闭相同的连接   命名空间,如果它是

的名称      
      
  • [...]
  •   
  • 一个模板。
  •   

因此,由于两个模板共享一个名称,这意味着[basic.def.odr]/5适用:

  

[...]类模板可以有多个定义   (条款[temp])[...]在一个程序中提供了每个定义   出现在不同的翻译单元中,并提供了定义   满足以下要求。鉴于这样一个名为D的实体   在多个翻译单元中定义,然后

     
      
  • D的每个定义应由相同的令牌序列组成;和
  •   
  • [...]
  •   
     

如果D是模板并且在多个翻译单元中定义,   那么前面的要求应适用于   模板定义中使用的模板封闭范围   ([temp.nondep]),以及相关的名字   实例化([temp.dep])。如果D的定义满足所有这些   要求,那么程序应该表现得好像有一个单一的   D.的定义如果D的定义不满足这些   要求,那么行为是未定义的。

边距不一样的令牌序列。

通过将模板的两个定义放入一个未命名的命名空间,您可以轻松地解析它,如Jarod42所示,从而为它们提供内部链接。