最Vexing的朋友?朋友化一个专门的免费函数模板会引发编译错误(当重载一个方法时)

Most Vexing Friend ? Friend-ing a specialized free-function template raises compilation error (when overloading a method)

本文关键字:一个 朋友 错误 编译 方法 重载 Vexing 函数模板 免费      更新时间:2023-10-16

代码

我将问题简化为这个例子(粘贴为单个块以便于编译)

/// brief The free-function template,
/// which is overloading a method with the same name in AbstractA below.
template <class T>
inline const T overloadedMethod(const T& lhs, const T& rhs)
{
    return T(lhs.value+rhs.value);
}
/// brief AbstractA class
class AbstractA
{
public:
    AbstractA (int aVal):
      value(aVal)
      {}

      inline const AbstractA overloadedMethod(const AbstractA &rhs) const
      {
          return AbstractA(value+rhs.value);            
      }
protected:
    int value;
};
/// brief A class, deriving from AbstractA,
/// and friending the free-function template.
class A : public AbstractA
{   
    friend const A overloadedMethod <A>(const A& lhs, const A& rhs);
        /// This one gives me compilation error
    //template<class T> friend const T overloadedMethod(const T& lhs, const T& rhs);
        /// This one would be okay
public:
    A (int aVal):
      AbstractA(aVal)
      {}
};
int main()
{
   A a1(1), a2(2);
   overloadedMethod(a1, a2);
   return 0;
}

详细信息

基本上,我尝试的编译器(VS 2010和G++4.7.2)在行上给了我一个错误

friend const A overloadedMethod <A>(const A& lhs, const A& rhs);

他们似乎认为我在声明一个名为overloadedMethod的数据成员。

如果出现以下情况,则不会引发编译错误:

  • 我把免费函数模板的非专业化版本作为朋友(评论了代码行)
  • 我从类AbstractA中删除了成员函数overloadedMethod()

问题

我无法解释langage的这种行为,所以我的问题是:

  • 导致此错误的C++规则是什么?(为什么编译器会认为我在这里声明了一个数据成员?)
  • 你知道背后的理由吗?(我特别想知道,如果我从类AbstractA中删除重载的Method(),它为什么会起作用。还是UB?)

首先,您的friend声明的基本前提是健全的:

[C++11: 14.5.4/1]:类或类模板的朋友可以是函数模板或类模板,函数模板或类别模板的专业化,也可以是普通(非模板)函数或类。对于不是模板声明的友元函数声明:

  • 如果友元的名称是合格或不合格的模板id,则友元声明指的是函数模板的专用化,否则
  • 如果友元的名称是限定id,并且在指定的类或命名空间中找到匹配的非模板函数,则友元声明引用该函数,否则
  • 如果友元的名称是一个限定id,并且在指定的类或命名空间中找到了匹配的函数模板,则友元声明指的是该函数模板的推导专用化(14.8.2.6),否则
  • 名称应为声明(或重新声明)普通(非模板)函数的非限定id

[示例:

template<class T> class task;
template<class T> task<T>* preempt(task<T>*);
template<class T> class task {
friend void next_time();
friend void process(task<T>*);
friend task<T>* preempt<T>(task<T>*);
template<class C> friend int func(C);
friend class task<int>;
template<class P> friend class frd;
};

[..]—结束示例]

您可能会遇到问题,因为基类中的名称overloadedMethod隐藏了全局名称—不管不同的参数列表,以及基本名称不代表模板的事实:

[C++11: 3.4.1/9]:在授予友谊的类中内联定义的朋友函数(11.3)的定义中使用的名称的名称查找应按照成员函数定义中的查找所述进行如果在授予友谊的类中没有定义好友函数,则好友函数定义中的名称查找应按描述用于在命名空间成员函数定义中查找

[C++11: 3.4.1/10]:在命名成员函数的friend声明中,函数声明符中使用的名称,而不是声明符id模板参数的一部分,将首先在成员函数的类的范围中查找(10.2)。如果找不到该名称,或者该名称是宣告符id模板参数的一部分,在授予友谊的类的定义中,查找是对不合格名称的描述。

在这种情况下,"如果找不到"子句永远不会被触发。

在GCC 4.8.1中,这会导致以下诊断:

错误:字段"overloadedMethod"的类型不完整

我确信这个诊断的具体内容有点错误—通过将模板参数列表<A>应用于它不认为是模板的东西,您基本上混淆了编译器的糟糕之处。

即使通过限定friend声明也无法解决此问题:

friend const A ::overloadedMethod<A>(const A& lhs, const A& rhs);

以下内容确实有效:

friend auto ::overloadedMethod<A>(const A&, const A&) -> const A;

但基于上述规则,我认为这实际上是一个编译器错误。

相关文章: