Bug 42328 - rejects valid friend
Summary: rejects valid friend
Status: RESOLVED WORKSFORME
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.4.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-12-07 23:04 UTC by Ivan Godard
Modified: 2015-03-28 10:29 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work: 4.8.0, 4.9.0, 5.0
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ivan Godard 2009-12-07 23:04:11 UTC
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.
Comment 1 Andrew Pinski 2009-12-07 23:13:54 UTC
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();
                            ^
Comment 2 Paolo Carlini 2009-12-07 23:35:29 UTC
Likewise SunStudio and Icc reject it.
Comment 3 Ivan Godard 2009-12-08 02:16:10 UTC
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.

Comment 4 Jonathan Wakely 2009-12-08 09:57:04 UTC
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();
    };


Comment 5 Ivan Godard 2009-12-08 15:20:18 UTC
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.
Comment 6 Jonathan Wakely 2014-03-21 15:53:30 UTC
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
Comment 7 Richard Smith 2014-12-04 00:39:00 UTC
(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();
        ~~~~~~~~~~~~~~~~~~^
Comment 8 Paolo Carlini 2015-03-27 18:10:52 UTC
Thus, I understand we could close this as worksforme, Richard?
Comment 9 Richard Smith 2015-03-27 18:47:07 UTC
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.
Comment 10 Paolo Carlini 2015-03-27 19:39:48 UTC
Thanks Richard. Then I'm going to add both testcases before closing the bug.
Comment 11 paolo@gcc.gnu.org 2015-03-28 10:28:46 UTC
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
Comment 12 Paolo Carlini 2015-03-28 10:29:37 UTC
Done.