跨TU的类模板重载

rus*_*tyx 8 c++ language-lawyer c++11

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

A.cpp:

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

int testA() {
    return Shape<int>{2}.area();
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

Main.cpp的:

int testA();
int testB();
int main() {
   return testA() + testB();
}
Run Code Online (Sandbox Code Playgroud)

虽然它编译(只要A和B是在单独的TU),它看起来不正确,和我有麻烦弄清楚为什么.

因此,我的问题:这是否违反ODR,超载,或任何其他规则,如果是这样,什么部分标准受到侵犯,为什么?

Sto*_*ica 13

这是ODR违规.模板名称具有链接.这两个模板名称都有外部链接,[basic.link]/4表示:

未命名的命名空间或在未命名的命名空间中直接或间接声明的命名空间具有内部链接.所有其他名称空间都有外部链接.具有名称空间作用域的名称上面没有给出内部链接,如果是名称,则具有与封闭名称空间相同的链接

  • [...]
  • 一个模板.

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

在程序中可以有多个[...]类模板(Clause [temp])[...]的定义,前提是每个定义出现在不同的翻译单元中,并且定义满足以下要求.鉴于这样一个名为D的实体在多个翻译单元中定义,那么

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

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

边距不一样的令牌序列.

正如Jarod42建议的那样,您可以通过将模板的两个定义放入未命名的命名空间来轻松解决它,从而为它们提供内部链接.