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;
}
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 am not able to explain this behavior of the langage, so my questions would be :
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 afriend
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.