4

代码

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

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

他们似乎认为我在声明一个名为的数据成员重载方法。

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

  • 我把自由功能模板的非专业版作为朋友(注释的代码行)
  • 我从类AbstractA中删除了成员函数overloadedMethod()

问题

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

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

1 回答 1

4

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

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

  • 如果朋友的名字是一个合格或不合格的模板ID,朋友声明是指一个函数模板的特化,否则
  • 如果朋友的名字是一个qualified-id,并且在指定的类或命名空间中找到了一个匹配的非模板函数,则朋友声明引用该函数,否则,
  • 如果朋友的名字是一个qualified-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命名成员函数的声明中,首先在成员函数类(10.2)的范围内查找函数声明符中使用的名称,而不是声明符 id中模板参数的一部分。如果未找到,或者该名称是declarator-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;

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

于 2013-08-07T09:58:37.973 回答