下面的代码编译后没有出现任何错误,但它似乎破坏了ODR:
#include <iostream>
template<long Num>
class B;
template<long Num>
struct A {
template<long Num1>
friend void ffoo(A<Num1> a, B<Num>* = nullptr) {
std::cout << "@A ffoo(A<" << Num1 << ">, B<" << Num << ">*)" << std::endl;
}
};
template<long Num>
class B {
public:
friend void ffoo(A<Num> a, B<Num>* = nullptr) {
std::cout << "@B ffoo(A<" << Num << ">, B<" << Num << ">*)" << std::endl;
}
};
int main() {
ffoo(A<1>{}); // @A ffoo(A<1>, B<1>*)
B<1>* ptr = nullptr;
ffoo(A<1>{}, ptr); // @B ffoo(A<1>, B<1>*)
}
ODR规则允许ODR中断为IFNDR(格式不正确,不需要诊断)的情况,所有这些情况似乎都与具有多个翻译单元的程序有关。
第一段非常清楚地说明了单一翻译单位的要求:
[basic.def.odr]/1
任何翻译单元都不得包含任何变量,函数,类类型,枚举类型,模板,参数的默认实参(对于给定作用域中的函数)或默认模板实参的一个以上定义。
上面的代码会打破ODR吗? 如果是这样,在单个翻译单元中破坏ODR是否需要编译器诊断?
*注意:代码示例中的friend模板函数似乎符合[temp.inst]的新规则。
b
的友元不是函数模板。 这个朋友声明不是模板声明,所以我们有
[Temp.Friend](强调我的)
1类或类模板的朋友可以是函数模板或类模板,函数模板或类模板的专门化,也可以是非模板函数或类。 对于不是模板声明的友元函数声明:
>
如果友元的名称是限定的或非限定的模板ID,则友元声明引用函数模板的专门化,否则,
如果友元的名称是限定ID,并且在指定的类或命名空间中找到匹配的非模板函数,则友元声明引用该函数,否则,
如果friend的名称是限定ID,并且在指定的类或命名空间中找到匹配的函数模板,则friend声明引用该函数模板的推导专门化([temp.dedt.decl]),否则,
名称应为声明(或重新声明)非模板函数的不合格的-ID。
因此ffoo
的两个声明并不声明同一个实体。 一个是函数模板,另一个是非模板函数。 这两个可能与重载存在于同一个声明性区域中。
所以这里没有违反ODR。