为什么我不能在模板化类中内联定义非模板化友元?

Why can't I inline-define a non-templated friend within a templated class?

本文关键字:定义 友元 不能 为什么      更新时间:2023-10-16

mcve的说话胜于雄辩:

// int bar();
template <bool B> class Foo {
    friend int ::bar() { return 123; }
};
int main()
{
    Foo<false> f1;
    Foo<true> f2;
}

使用GCC 6和--std=c++14,这给了我:

a.cpp: In instantiation of ‘class Foo<true>’:
a.cpp:9:12:   required from here
a.cpp:3:13: error: redefinition of ‘int bar()’
  friend int ::bar() { return 123; }
             ^~
a.cpp:3:13: note: ‘int bar()’ previously defined here

现在,我不确定标准怎么说;但是我知道编译器知道朋友没有在B上模板,其定义也不使用B。那么,为什么它不能应用"哦,函数相同定义的所有内联副本都是相同的"规则?

现在,我不确定标准怎么说;

实际上已经在即将到来的C 17

中阐明了一个示例。

[temp.inst]/2类模板专业化的隐式实例... [snip] ...为了确定成员的实例化重新分支是否有效,根据3.2 [basic.def.odr]和9.2 [class.mem],与模板中定义相对应的声明被认为是一个定义。[示例:

... [SNIP(另一个示例)] ...

template<typename T> struct Friendly {
  template<typename U> friend int f(U) { return sizeof(T); }
};
Friendly<char> fc;
Friendly<float> ff; // ill-formed: produces second definition of f(U)

- 结束示例]

诚然,正如您指出的那样,标准的示例确实为每个实例化产生了不同的定义,但这对于示例并不需要根据该规则进行不良。blockquote>

那么,为什么不能应用"哦,函数相同定义的所有内联副本都是相同的"规则?

这个问题似乎也适用于更简单的情况:

inline void foo(){}
inline void foo(){}

肯定可以看到定义是相同的,就像编译器可以看到::bar的定义不取决于Foo的模板参数。

然而,ODR说重新定义是不明式的。对于类模板之外的定义以及由类模板的实例化引起的定义是正确的。


也许ODR 可以放松您所证明的情况,但这需要使用特殊案例规则使标准复杂化,并使编译器复杂化,然后必须分析是否使用模板参数在定义中,因此这种放松当然并非没有妥协。