在一个 TU 内破坏 ODR?

Breaking of ODR inside one TU?

以下代码 compiles without any error,虽然它似乎破坏了 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 Rules 允许打破 ODR 的情况是 IFNDR(格式错误,不需要诊断)所有这些情况似乎都与具有多个翻译单元的程序有关。

first paragraph 非常清楚地说明了单个翻译单元的要求:

[basic.def.odr]/1

No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, template, default argument for a parameter (for a function in a given scope), or default template argument.

以上代码是否破坏了 ODR?如果是这样,打破单个翻译单元内的 ODR 是否需要编译器诊断?


* 注意:代码示例中的 friend 模板函数 似乎确实遵守新规则共 [temp.inst].

B 的好友不是函数模板。友元声明不是模板声明,所以我们有

[temp.friend] (emphasis mine)

1 A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or a non-template function or class. For a friend function declaration that is not a template declaration:

  • if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,

  • if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,

  • if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template ([temp.deduct.decl]), otherwise,

  • the name shall be an unqualified-id that declares (or redeclares) a non-template function.

所以 ffoo 的两个声明没有声明同一个实体。一种是函数模板,另一种是非模板函数。这两个可能存在于与重载相同的声明区域中。

所以这里没有ODR违规。