Fails to identify overload, although it lists the overload it should be identifying right in the error message.
Created attachment 6555 [details] Compiler output (-v -save-temps)
Created attachment 6556 [details] Source code (-save-temps)
(You should really learn to write _small_ testcases -- you'd save everyone a lot of time that way...) Here's a reduced testcase: ---------------------- class B {}; template <typename> struct A { template <typename U> friend void operator<< (A &, const U &); }; template<typename T> void operator<<(A<T> &, const B &); int main() { A<int> d; d << B(); } ----------------------------- With gcc, we get this error: g/x> /home/bangerth/bin/gcc-3.4-pre/bin/c++ -w -c x.cc x.cc: In function `int main()': x.cc:13: error: ambiguous overload for 'operator<<' in 'd << B()' x.cc:9: note: candidates are: void operator<<(A<T>&, const B&) [with T = int] x.cc:5: note: void operator<<(A< <template-parameter-1-1> >&, const U&) [with U = B, <template-parameter-1-1> = int] I think gcc is completely correct in printing this error: both functions are exact matches, but only after template parameter unification. Why do you think that gcc should accept the code? W. PS: With icc, we get this: g/x> icc -c -Xc -ansi x.cc x.cc(13): error: more than one operator "<<" matches these operands: function template "operator<<(A<int> &, const U &)" function template "operator<<(A<T> &, const B &)" operand types are: A<int> << B d << B(); ^ compilation aborted for x.cc (code 2)
Well, as I understand it, an exact match on a non-template argument trumps any template match in the same position of an overload; this is the essence of specialization. I also understand that a "friend" declaration within a class is the equivalent (for identification purposes) to the same declaration outside the class with appropriate substitutions, i.e. friendship is irrelevant for call indentification, and only matters in the body. In your reduced case, after removing the "friend" to outside the class manually (to mimic export of friends as performed by the compiler), your example becomes equivalent to: class B {}; template <typename> struct A { }; template <typename T, typename U> void operator<< (A<T> &, const U &); template<typename T> void operator<<(A<T> &, const B &); int main() { A<int> d; d << B(); } which is exactly what you would have written had you *not* had to make one of the overloads a friend. Expessed in this form it is clear that the latter overload is essentially a specialization of the former and the call should identify the latter. Which in fact it does if you compile the rewritten case. Now it seems to me that identification should not depend upon whether a function had been made a friend or not. I don't understand the standard enough to know whether my two functions (as rewritten to remove "friend") are ambiguous, but if they are they should be ambiguous in both your version and my rewrite. Or neither. Half and half is unreasonable, as these are clearly the same declarations. I believe (in my ignorance) that the compiler has it right on the rewritten version, and is failing to correctly export the friend to the surrounding scope in your version; considering the number of other bugs in friend export this would not be surprising. Ivan
In fact with "Comeau C++ Online", it also rejects reduced code so this is not a bug. The issue is more complex than what you said for friends as templates are involved.
icc 8, on the other hand accepts it. Initially, I had thought that in both cases of my example in comment #3 the compiler has to match exactly one template argument. In that case, an ambiguity would have been unavoidable. In fact, however, it has to match two template arguments for the friend function, and one for the non-friend. That's a case where partial ordering comes into play. I am not sure any more whether my analysis of my testcase is correct, and would like to solicit other opinions. W.
There are 2 functions involved here: 1. template <typename U> void operator<< (A<int> &, const U &); which is generated by the compiler when instantiating A<int>. 2. template<typename T> void operator<< (A<T> &, const B &); So you have ambiguity here. Note that the template friend does not mean this function: template <typename T, typename U> void operator<< (A<T> &, const U &);