考虑以下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的哪些部分会被违反?为什么?
答案 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所示,从而为它们提供内部链接。