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

时间:2017-09-24 23:14:36

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

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

1 个答案:

答案 0 :(得分:5)

  

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

事实上,这个案例已经在即将到来的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)
     

- 结束示例]

不可否认,正如您所指出的那样,标准的示例确实为每个实例化生成了不同的定义,但是根据该规则,该示例不是必需的。

  

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

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

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

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

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

对于您演示的案例,或许可以放宽,但这需要将标准与特殊案例规则复杂化,并使编译器复杂化,然后必须分析是否使用模板参数在定义范围内,所以这种放松当然不会妥协。

相关问题