This code: template<typename T, typename U> class freeList { public: void foo() {}; }; class bar {}; class baz : protected freeList<bar, baz> { template<typename T> friend void freeList<T, baz>::foo(); }; int main() { baz b; return 0; } gets you: s3:~/ootbc/personal/ivan$ g++ foo.cc foo.cc:12: error: member ‘void freeList<T, baz>::foo()’ declared as friend before type ‘freeList<T, baz>’ defined FWIW, Comeau accepts it.
Comeau C++ also rejects this code with the following error message: "ComeauTest.c", line 12: error: declaration is incompatible with nontype "<unnamed> freeList<T, U>::foo [with T=T, U=baz]" (declared at line 12) void freeList<T, baz>::foo(); ^
Likewise SunStudio and Icc reject it.
I stand corrected about Comeau - I missed the error and only noticed the warning. Regardless, there must be some way to solve this problem: template<typename T, typename U> class freeList { public: void foo(); }; class bar {}; class baz : protected freeList<bar, baz> { void frob() {} }; template<> void freeList<bar, baz>::foo() { static_cast<baz*>(this)->frob(); } int main() { baz b; return 0; } gets you: s3:~/ootbc/personal/ivan$ g++ foo.cc foo.cc: In member function ‘void freeList<T, U>::foo() [with T = bar, U = baz]’: foo.cc:14: error: ‘freeList<bar, baz>’ is an inaccessible base of ‘baz’ I believe this diagnostic is legitimate. If I recall, in earlier versions of the compiler (4.0.X? Don't remember) the template "friend" declaration in the original report exposed the base class to the friend function and the code (with the friend) compiled without error. Then we upgraded the compiler and it stopped working. I thought this reflected a new bug and reported it. If in fact the friend is invalid (just not previously caught) then fine - but then how do you get the original cast to the base to work? In short, if the friend declaration I wrote is illegal, how otherwise do you turn a member function of a base class into a friend of a derived class of that base? I realize that helping me in my code is not your job, but any help would be greatly appreciated. And if it is not in fact possible perhaps some of the language mavens among you might raise the issue with the standards group, because what I was trying is certainly a reasonable thing to want to do.
What's wrong with class baz : protected freeList<bar, baz> { void frob() {} friend class freeList<bar, baz>; }; or class baz : protected freeList<bar, baz> { void frob() {} template<typename T, typename U> friend void freeList<T, U>::foo(); };
Both proposals befriend more than the minimum actually needed by program semantics. The former befriends any member of freeList<bar, baz>, not just foo; the latter any member of any freeList at all. In addition, the former requires that bar be a resolved type. Bar is resolved in the simplified example I submitted, but in the original baz is itself a template and bar is a class argument, and you get a diagnostic on a friend of the form of the first suggestion. That's why the friend is a template, not just freeList<bar, baz> - the original code was more like: template<typename U> class baz : protected freeList<U, baz>, protected freeList<aType, baz>, protected freeList<bType, baz>, < more, where all but U were resolved types > { template<typename T> friend void freeList<T, baz>::foo(); < body of baz > }; but was simplified for the report.
Clang accepts the code in comment 0, but GCC 4.9 and icc 13.0.1 still give the same errors as stated here in 2009
(In reply to Jonathan Wakely from comment #6) > Clang accepts the code in comment 0 Clang is cheating; it says: <stdin>:12:27: warning: dependent nested name specifier 'freeList<T, baz>::' for friend class declaration is not supported; turning off access control for 'baz' [-Wunsupported-friend] void freeList<T, baz>::foo(); ~~~~~~~~~~~~~~~~~~^
Thus, I understand we could close this as worksforme, Richard?
Yes, I think this is ill-formed (although the standard is very unclear about this). I think the declaration template<typename T> friend void freeList<T, baz>::foo(); would declare the member 'foo' of the class template partial specialization freeList<T, baz> to be a friend, but no such partial specialization has been defined. If I add class baz; template<typename T> class freeList<T, baz> { void foo(); }; before the definition of 'baz', then GCC and EDG accept, which I think is correct behavior.
Thanks Richard. Then I'm going to add both testcases before closing the bug.
Author: paolo Date: Sat Mar 28 10:28:14 2015 New Revision: 221751 URL: https://gcc.gnu.org/viewcvs?rev=221751&root=gcc&view=rev Log: 2015-03-28 Paolo Carlini <paolo.carlini@oracle.com> PR c++/42328 * g++.dg/template/friend58.C: New. * g++.dg/template/friend59.C: Likewise. Added: trunk/gcc/testsuite/g++.dg/template/friend58.C trunk/gcc/testsuite/g++.dg/template/friend59.C Modified: trunk/gcc/testsuite/ChangeLog
Done.