简体   繁体   中英

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

Code

I reduced the problem to this example (pasted as a single block for ease of compilation)

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

Details

Basically, the compilers I tried (VS 2010 and G++ 4.7.2) give me an error on the line

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

They seem to think I am declaring a data member named overloadedMethod .

The compilation error is not raised if :

  • I give the non-specialized version of the free-function template as a friend (commented line of code)
  • I remove the member function overloadedMethod() from class AbstractA

Questions

I am not able to explain this behavior of the langage, so my questions would be :

  • What is the C++ rule that is leading to this error ? (Why would compilers think I am declaring a data member here ?)
  • Do you know a rationale behind it ? (I am especially curious to know why it seems to be working if I remove the overloadedMethod() from class AbstractA . Or is it an UB ?)

First of all, the basic premise of your friend declaration is sound:

[C++11: 14.5.4/1]: A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or an ordinary (non-template) function or class. For a friend function declaration that is not a template declaration:

  • if the name of the friend is a qualified or unqualified template-id , the friend declaration refers to a specialization of a function template , otherwise
  • if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,
  • if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template (14.8.2.6), otherwise,
  • the name shall be an unqualified-id that declares (or redeclares) an ordinary (non-template) function.

[ Example:

 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; }; 

[..] —end example ]

You can run into problems because the name overloadedMethod from the base class hides the global one — irrespective of the different argument list, and the fact that the base name does not represent a template:

[C++11: 3.4.1/9]: Name lookup for a name used in the definition of a friend function (11.3) defined inline in the class granting friendship shall proceed as described for lookup in member function definitions. If the friend function is not defined in the class granting friendship, name lookup in the friend function definition shall proceed as described for lookup in namespace member function definitions.

[C++11: 3.4.1/10]: In a friend declaration naming a member function, a name used in the function declarator and not part of a template-argument in the declarator-id is first looked up in the scope of the member function's class (10.2). If it is not found, or if the name is part of a template-argument in the declarator-id , the look up is as described for unqualified names in the definition of the class granting friendship.

The "if it is not found" clause can never be triggered in this case.

In GCC 4.8.1 this results in the following diagnostic :

error: field 'overloadedMethod' has incomplete type

I'm sure that the specific contents of this diagnostic are slightly erroneous — you've basically confused the heck out of your compiler, by applying the template-parameter-list <A> to something that it does not believe is a template.

You cannot fix this, even by qualifying the friend declaration :

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

The following does work:

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

But I think that this is actually a compiler bug, based on the above rules.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM