Topic: friend class as forward declaration


Author: Martin von Loewis <loewis@informatik.hu-berlin.de>
Date: 1999/03/22
Raw View
jgro@netcom.com (Jeremy) writes:

> class E {
>   private:
>     class B { int x; };
>     friend class E::C;  // Error: E::C not defined
>     class C { B b; };
> };
>
> Well that is what has me confused.  We are inside class E, why doesn't
> that work?

I assume you refer to the feature 'use before declaration' for
members. Well, this applies only to names used in member functions,
not names used to declare, e.g., friend classes. See
[basic.lookup.unqual]/7 and [basic.lookup.unqual]/8 for details.


> I found this is Stroustrup:
>
> class E {
>   private:
>     class B { int x; };
>     class C;
>     friend class C;
>     class C { B b; };
> };
>
> and that works, but I'm confused as to why the forward declaration of
> class C is needed and why it works.

You need the class to be declared before you can declare it as a
friend class (except if you are declaring a namespace-scoped friend).
So you first forward-declare the class, then declare it friend, and
then define it.

Hope this helps,
Martin


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: jgro@netcom.com (Jeremy)
Date: 1999/03/22
Raw View
In article <p6qn215odxj.fsf@pandora.inst-inf-1.hu-berlin.de>,
Martin von Loewis  <loewis@informatik.hu-berlin.de> wrote:
>
>jgro@netcom.com (Jeremy) writes:
>
>> class E {
>>   private:
>>     class B { int x; };
>>     friend class E::C;  // Error: E::C not defined
>>     class C { B b; };
>> };
>>
>> Well that is what has me confused.  We are inside class E, why doesn't
>> that work?
>
>I assume you refer to the feature 'use before declaration' for
>members. Well, this applies only to names used in member functions,
>not names used to declare, e.g., friend classes. See
>[basic.lookup.unqual]/7 and [basic.lookup.unqual]/8 for details.
>

No, I refer to the feature that you allude to below. But since you
bring it up, from my reading of [basic.lookup], you should no longer
be able to do this:

class A {
  friend class B;  // not previously declared
  ...
};

for the same reasons.  Has this been taken out of the language?  If
this is the case, then I guess that is the source of my confusion.
The compiler I'm using still accepts it.


>
>> I found this is Stroustrup:
>>
>> class E {
>>   private:
>>     class B { int x; };
>>     class C;
>>     friend class C;
>>     class C { B b; };
>> };
>>
>> and that works, but I'm confused as to why the forward declaration of
>> class C is needed and why it works.
>
>You need the class to be declared before you can declare it as a
>friend class (except if you are declaring a namespace-scoped friend).


OK, so where in the spec does it say that.  I spent hours looking
through it before posting the orginal question and found neither the
statement that you could declare as a friend a namespace-scoped class
that had not been previously declared nor the statement that this
ability was limited to namespace-scoped classes.   I was relying on
the pre-standard ARM where the implication was that the "friend class"
declaration did double duty as an implicit forward declaration.

I'm not complaining about the results, I'm sincerely asking for help
making sense of the spec in this regard.  For example, [dcl.type.elab]
explains explicitly that "friend class E::C;" is a syntatically
legitimate declaration as well as as a legitimate elaborated type
specifier, but I cannot find anything that makes the distinction
between "friend class E::C;" and "friend class C;".
--
Jeremy Grodberg
jgro@netcom.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: scott douglass <sdouglass%_%junk@_.arm.com>
Date: 1999/03/23
Raw View
Jeremy wrote:
> I'm not complaining about the results, I'm sincerely asking for help
> making sense of the spec in this regard.  For example, [dcl.type.elab]
> explains explicitly that "friend class E::C;" is a syntatically
> legitimate declaration as well as as a legitimate elaborated type
> specifier, but I cannot find anything that makes the distinction
> between "friend class E::C;" and "friend class C;".

7.1.5.3 says, in part:
>>
If name lookup does not find a declaration for the name, the
elaborated-type-specifier is ill-formed unless it is of the simple form
class-key identifier in which case the identifier is declared as described in
3.3.1.

This is what makes the distinction between "friend class E::C;" and "friend
class C;".  But then things get stranger:
<<

3.3.1 says that it doesn't apply to 'friend class identifier;', in part:
>>
if the elaborated-type-specifier is used in the decl-specifier-seq or
parameter-declaration-clause of a function defined in namespace scope, the
identifier is declared as a class-name in the namespace that contains the
declaration; otherwise, except as a friend declaration, the identifier is
declared in the smallest non-class, non-function-prototype scope that contains
the declaration.

[Note: friend declarations refer to functions or classes that are members of the
nearest enclosing namespace, but they do not introduce new names into that
namespace (7.3.1.2). ...]
<<

It would be nice if 3.3.1 has a cross-reference to 11.4.  But because everyone's
read and memorized the whole standard we remember that 3.3 says, in part:
>>
The names declared by a declaration are introduced into the scope in which the
declaration occurs, except that the presence of a friend specifier (11.4),
certain uses of the elaborated-type-specifier (3.3.1), and using-directives
(7.3.4) alter this general behavior.
<<

And finally 11.4 says, in part:
>>
For a friend class declaration, if there is no prior declaration, the class that
is specified belongs to the innermost enclosing non-class scope, but if it is
subsequently referenced, its name is not found by name lookup until a matching
declaration is provided in the innermost enclosing nonclass scope.
<<

This is followed an example that is not particularly relaevnt here.

Hope this clears things up -- as much as they can be anyway.
 --scott


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Martin von Loewis <loewis@informatik.hu-berlin.de>
Date: 1999/03/23
Raw View
jgro@netcom.com (Jeremy) writes:

> class A {
>   friend class B;  // not previously declared
>   ...
> };
>
> for the same reasons.  Has this been taken out of the language?

No, it's still there as a special case. [dcl.type.elab]/3 says

# If name lookup does not find a declaration for the name, the
# elaborated   type   specifier is ill   formed unless it is of the simple
# form class   key identifier in which case the identifier is declared
# as described in 3.3.1.

> >You need the class to be declared before you can declare it as a
> >friend class (except if you are declaring a namespace-scoped friend).
>
> OK, so where in the spec does it say that.

The paragraph cited above does. It is somewhat more elaborate (sic),
and explains that it is also ill-formed if lookup finds a typedef or a
template parameter.

> I'm not complaining about the results, I'm sincerely asking for help
> making sense of the spec in this regard.  For example, [dcl.type.elab]
> explains explicitly that "friend class E::C;" is a syntatically
> legitimate declaration as well as as a legitimate elaborated type
> specifier, but I cannot find anything that makes the distinction
> between "friend class E::C;" and "friend class C;".

I'd say "unless it is of the simple form class   key identifier" is a
good phrase to make that distinction :-) especially when the text
above lists the alternatives

class   key identifier ;
friend class   key identifier ;
friend class   key ::identifier ;
friend class   key nested   name   specifier identifier ;

This raises another question: What is the reason that

namespace A{
  class B;
}
class C{
  friend class ::A::B;
};

is ill-formed (*)?

Regards,
Martin

(*) According to the FDIS, at least... nested-name-specifier does
start with with a class-or-namespace-name, doesn't it?


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: jgro@netcom.com (Jeremy)
Date: 1999/03/24
Raw View
In article <p6q7ls8xe7d.fsf@pandora.inst-inf-1.hu-berlin.de>,
Martin von Loewis  <loewis@informatik.hu-berlin.de> wrote:
>
>jgro@netcom.com (Jeremy) writes:
>
>> class A {
>>   friend class B;  // not previously declared
>>   ...
>> };
>>
>> for the same reasons.  Has this been taken out of the language?
>
>No, it's still there as a special case. [dcl.type.elab]/3 says
>
># If name lookup does not find a declaration for the name, the
># elaborated-type-specifier is ill-formed unless it is of the simple
># form class-key identifier in which case the identifier is declared
># as described in 3.3.1.

[[snip]]

>> ... but I cannot find anything that makes the distinction
>> between "friend class E::C;" and "friend class C;".
>
>I'd say "unless it is of the simple form class-key identifier" is a
>good phrase to make that distinction :-) especially when the text
>above lists the alternatives
>
>class-key identifier ;
>friend class-key identifier ;
>friend class-key ::identifier ;
>friend class-key nested-name-specifier identifier ;
>

But the simple form "class-key identifier" is not "friend class-key
identifier."   They are listed separately in the text you cite and
3.3.1 also seems to make a clear destinction between the two. Heck,
3.3.1 even distinguishes between "class-key identifier" and "class-key
identifier ;" so you can see why I might take it very literally.

So am I correct that I should henceforth read the phrase "the simple
form class-key identifier" to mean either "class X" or "friend class X"?





--
Jeremy Grodberg
jgro@netcom.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Martin von Loewis <loewis@informatik.hu-berlin.de>
Date: 1999/03/24
Raw View
jgro@netcom.com (Jeremy) writes:

> But the simple form "class-key identifier" is not "friend class-key
> identifier."   They are listed separately in the text you cite and
> 3.3.1 also seems to make a clear destinction between the two. Heck,
> 3.3.1 even distinguishes between "class-key identifier" and "class-key
> identifier ;" so you can see why I might take it very literally.
>
> So am I correct that I should henceforth read the phrase "the simple
> form class-key identifier" to mean either "class X" or "friend class X"?

No, it says that the 'elaborated=ADtype=ADspecifier is ill=ADformed unless
it is of the simple formed ...'. Please re-read the syntax for
elaborated-type-specifier; it does not contain 'friend'. So the
*declaration*

   friend class X;

contains the *e-t-s* in the 'simple form'. As for 3.3.1, this
declaration does not have the form

   class X;

therefore, the class is not introduced in the scope where the friend
declaration appears. The e-t-s also doesn't appear in a 'function
defined in namespace scope', so this declaration clearly declares an
identifier 'in the smallest non=ADclass, non=ADfunction=ADprototype scope
that contains the declaration', which is namespace scope.

Hope this helps,
Martin
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jgro@netcom.com (Jeremy)
Date: 1999/03/19
Raw View

I've pored through the ISO standard, looked through The C++
Programming Language, Third Edition, by Stroustrup, but I still can't
figure out the rules about using "friend" as a forward declaration.

I started out with this:

class E {
  private:
    class B { int x; };
    class C { B b; };  // Error, class E::B is private
};

I wanted it to compile, I wanted C to be a friend of E, so I tried
this:

class E {
  private:
    class B { int x; };
    friend class C;
    class C { B b; };
};

but that didn't work, the compiler still complained that E::C couldn't
access E::B.  That makes sense, "friend class C" forward declares ::C
not E::C.  So then I tried this:

************** Here is my question *****************
class E {
  private:
    class B { int x; };
    friend class E::C;  // Error: E::C not defined
    class C { B b; };
};

Well that is what has me confused.  We are inside class E, why doesn't
that work?

I found this is Stroustrup:

class E {
  private:
    class B { int x; };
    class C;
    friend class C;
    class C { B b; };
};

and that works, but I'm confused as to why the forward declaration of
class C is needed and why it works.


************** end question *****************


Any recommendations for better reference materials would also be
appreciated.

Thanks.
--
Jeremy Grodberg
jgro@netcom.com


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: stanley@west.sun.com (Stanley Friesen [Contractor])
Date: 1999/03/19
Raw View
In article <jgroF8tJBv.15C@netcom.com>, Jeremy <jgro@netcom.com> wrote:
>
>************** Here is my question *****************
>class E {
>  private:
>    class B { int x; };
>    friend class E::C;  // Error: E::C not defined
>    class C { B b; };
>};
>
>Well that is what has me confused.  We are inside class E, why doesn't
>that work?

A friend declaration isn't really a forward declaration.  It looks up the
class name using the usual lookup rules.  No such class is found.
>
>I found this is Stroustrup:
>
>class E {
>  private:
>    class B { int x; };
>    class C;
>    friend class C;
>    class C { B b; };
>};
>
>and that works, but I'm confused as to why the forward declaration of
>class C is needed and why it works.
>
The forward declaration introduces the class C into the scope of E.
The friend declaration, using the usual lookup rules, now finds the
nested class E::C.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]