【问题标题】:Why can't I inline-define a non-templated friend within a templated class?为什么我不能在模板类中内联定义非模板朋友?
【发布时间】:2017-09-24 23:14:36
【问题描述】:

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

【问题讨论】:

  • 不确定标准是怎么说的,但这里的定义似乎不是inline。尝试添加inline,看看是否有效?
  • 为什么不在类模板之外定义呢?
  • @Barry:这对我来说很方便,因为我在班级附近有一些相关的定义。
  • @anatolyg:隐含的inline,因为它在类定义(IIANM)中;添加inline 没有帮助。
  • “那么为什么不能应用“哦,同一个函数定义的所有内联副本都是一样的”? ——即使可以,为什么会呢?在任何其他情况下,该标准是否允许在同一翻译单元中重复定义同一函数?这种情况有什么特别之处,应该区别对待,例如inline void f() {} inline void f() {}?

标签: c++ templates language-lawyer inline friend


【解决方案1】:

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

这个案例实际上已经通过即将发布的 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)

— 结束示例 ]

诚然,正如您所指出的,该标准的示例确实为每个实例化生成了一个不同的定义,但根据该规则,对于该示例的格式错误来说,这并不是必需的。 p>

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

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

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

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

然而,odr 表示重新定义的格式不正确。这适用于类模板之外的定义,以及由类模板的实例化引起的定义。


也许对于您演示的情况,可以放宽 odr ,但这需要使用特殊情况规则使标准复杂化,并使编译器复杂化,然后必须分析是否使用模板参数在定义范围内,因此这种放松当然不是没有妥协的。

【讨论】:

  • 对不起,这个例子与我所说的相反。在您的示例中,朋友是在 T 上模板化的。在我的示例中,它不是。如果你想确保是这样,你可以取消我的第一行的注释,但这并不重要。
  • @einpoklum 我明白你对这个例子的看法。然而,就规则而言,它没有任何改变。
  • @Barry 也许应该向 gcc 提交一个错误,因为它未能诊断出该错误(如果没有 :: 就无法重现 OP 问题,那么它也将是问题的答案)
  • @einpoklum 不,不是。朋友函数定义使用T这一事实与规则无关。
  • 最后一段是AFAIAC的实际答案。
猜你喜欢
  • 1970-01-01
  • 2016-10-29
  • 1970-01-01
  • 2014-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
相关资源
最近更新 更多