最烦人的朋友?使用专门的自由函数模板会引发编译错误(当重载方法时)

时间:2013-08-07 09:06:48

标签: c++ templates friend

代码

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

/// \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()

问题

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

  • 导致此错误的C ++规则是什么? (为什么编译器会认为我在这里声明了一个数据成员?)
  • 你知道背后的理由吗? (我特别好奇,如果我从类 AbstractA 中删除 overloadedMethod(),它似乎有效。或者它是UB吗?)

1 个答案:

答案 0 :(得分:4)

首先,friend声明的基本前提是合理的:

  

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

     
      
  • 如果朋友的姓名是合格或不合格的 template-id ,则朋友声明会引用功能模板的专业化,否则
  •   
  • 如果朋友的名字是 qualified-id 并且在指定的类或命名空间中找到匹配的非模板函数,则friend声明引用该函数,否则,
  •   
  • 如果朋友的名称是 qualified-id 并且在指定的类或命名空间中找到匹配的函数模板,则friend声明引用该函数模板的推导的特化(14.8。 2.6),否则,
  •   
  • 该名称应为 unqualified-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声明中,函数声明符中使用的名称,而不是 template-argument 的一部分> declarator-id 首先在成员函数的类(10.2)的范围内查找。如果找不到,或者名称是 declarator-id template-argument 的一部分,则查找的内容与定义中的非限定名称相同。授予友谊的阶级。

在这种情况下永远不会触发“如果找不到”子句。

在GCC 4.8.1中this results in the following diagnostic

  

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

我确信这个诊断的具体内容有点错误 - 你基本上混淆了你的编译器,通过应用 template-parameter-list <A>对于它不相信的东西是一个模板。

您无法解决此问题,even by qualifying the friend declaration

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

以下工作正常:

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

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