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

7

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的定义。那么为什么不能应用“哦,所有相同定义的内联副本的函数都是相同的”规则呢?


2
为什么不在类模板之外定义它呢? - Barry
@Barry:这对我很方便,因为我在类中有一些相关的定义。 - einpoklum
5
为什么不能应用“噢,所有内联副本的同一函数定义都是相同的”?即使它可以,为什么要这么做?标准是否允许在同一翻译单元中重复定义相同的函数?这种情况有何特殊之处,使其应该与例如“inline void f() {} inline void f() {}”不同对待? - user743382
根据[class.friend]/7,类中定义的友元函数隐式地成为内联函数。 - M.M
@hvd:“为什么不呢?”因为这样可以允许您在模板类中定义非模板友元,而现在您无法这样做。 - einpoklum
显示剩余3条评论
1个回答

5

现在,我不确定标准是什么;

事实上,这个案例已经在即将发布的C++17中通过一个示例得到了澄清。

[temp.inst]/2 The implicit instantiation of a class template specialization ... [snip] ... for the purpose of determining whether an instantiated redeclaration of a member is valid according to 3.2 [basic.def.odr] and 9.2 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [ Example:

... [snip (another example)] ...

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)

— end example  ]

诚然,正如你所指出的那样,标准的范例确实为每个实例提供了不同的定义,但是这并不是该规则被认为是不良形式的必要条件。

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

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

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

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

然而,odr规定重新定义是不合法的。这对于类模板外的定义以及由类模板实例化引起的定义都是正确的。


也许odr规则对于您展示的情况可以放宽,但这将需要用特殊情况规则复杂化标准,并且使得编译器必须分析定义中是否使用了模板参数,因此这种放宽肯定是有妥协的。


对不起,但这个示例与我所讨论的相反。在你的示例中,friend 是基于 T 的模板化。在我的示例中,它不是。如果您想确保这种情况,请取消我的第一行的注释,但这真的没关系。 - einpoklum
@einpoklum,我理解你的观点。然而就规则本身而言,这个例子并没有改变什么。 - eerorika
@Barry 或许应该向 GCC 提交一个错误报告,因为它未能诊断出这个错误(如果 OP 的问题没有 :: 也无法重现,则这也是对问题的回答)。 - M.M
@einpoklum 不是这样。友元函数定义使用T并不影响规则。 - Barry
最后一段是我认为的实际答案。 - einpoklum
...但我不喜欢这个答案 :-( - einpoklum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接