Problems with protected variables despite friends

E

Eric Lilja

Hello, consider this program:
class Outer
{
public:
class Base
{
friend class Outer;
public:
Base(int n) : n_(n) {}
protected:
int n_;
};

class Child1 : public Base
{
friend class Outer;
friend class Child2;
public:
Child1(int n) : Base(n) {}
};

class Child2 : public Base
{
friend class Outer;
public:
Child2(int n) : Base(n) {}

Child2(const Child1& rhs) : Base(rhs.n_) {}
};
};

int main()
{
Outer::Child1 c1(4711);
Outer::Child2 c2(c1);
}

It compiles with MSVC++ 8.0 SP1 and gcc 4.2.0. It does not compile
with gcc 3.4.4:
$ g++ -Wall -Wextra -std=c++98 -pedantic -g foo.cpp -o runme.exe
foo.cpp: In constructor `Outer::Child2::Child2(const Outer::Child1&)':
foo.cpp:10: error: `int Outer::Base::n_' is protected
foo.cpp:27: error: within this context

Also, worth mentioning is that gcc 4.2.0 doesn't need quite as many
friend declarations as MSVC++ 8.0 SP1 does...is this code valid
according to the standard and is it being rejected by the older gcc
because of a bug or a failure to follow the standard as closely as gcc
4.2.0?

It's not an academic question for me, I encountered this problem in a
real program and this my reduced test case.

- Eric
 
Z

Zeppe

Eric said:
Hello, consider this program:
class Outer
{
public:
class Base
{
friend class Outer;
public:
Base(int n) : n_(n) {}
protected:
int n_;
};

class Child1 : public Base
{
friend class Outer;
friend class Child2;
public:
Child1(int n) : Base(n) {}
};

class Child2 : public Base
{
friend class Outer;
public:
Child2(int n) : Base(n) {}

Child2(const Child1& rhs) : Base(rhs.n_) {}
};
};

int main()
{
Outer::Child1 c1(4711);
Outer::Child2 c2(c1);
}

It compiles with MSVC++ 8.0 SP1 and gcc 4.2.0. It does not compile
with gcc 3.4.4:
$ g++ -Wall -Wextra -std=c++98 -pedantic -g foo.cpp -o runme.exe
foo.cpp: In constructor `Outer::Child2::Child2(const Outer::Child1&)':
foo.cpp:10: error: `int Outer::Base::n_' is protected
foo.cpp:27: error: within this context

Also, worth mentioning is that gcc 4.2.0 doesn't need quite as many
friend declarations as MSVC++ 8.0 SP1 does...is this code valid
according to the standard and is it being rejected by the older gcc
because of a bug or a failure to follow the standard as closely as gcc
4.2.0?

I think so, as far as I know your program is correct for the standard.
To let the code compile with g++ 3.4, try considering a forward declaration:

class Child2;

class Child1 : public Base
{
friend class Outer;
friend class Child2;
public:
Child1(int n) : Base(n) {}
};

class Child2 : public Base
{
friend class Outer;
public:
Child2(int n) : Base(n) {}

Child2(const Child1& rhs) : Base(rhs.n_) {}
};

it should work.

Regards,

Zeppe
 
E

Eric Lilja

I think so, as far as I know your program is correct for the standard.
To let the code compile with g++ 3.4, try considering a forward declaration:

class Child2;

class Child1 : public Base
{
friend class Outer;
friend class Child2;
public:
Child1(int n) : Base(n) {}
};

class Child2 : public Base
{
friend class Outer;
public:
Child2(int n) : Base(n) {}

Child2(const Child1& rhs) : Base(rhs.n_) {}
};

it should work.

Regards,

Zeppe

That doesn't help at all and I think the friend declaration is a
forward declaration in itself.
 
Z

Zeppe

That doesn't help at all and I think the friend declaration is a
forward declaration in itself.

yes, it is, according to the standard. But in my system (linux + gcc
version 3.4.6), adding a further forward declaration it's enough to let
the program compile correctly (your original program didn't worked on my
gcc 2.4 as well). If the follow (copied and pasted from the source that
compiles on my gcc 3.4) still doesn't compile on your gcc 3.4, I don't
know what to suggest you...

class Outer
{
public:
class Base
{
friend class Outer;
public:
Base(int n) : n_(n) {}
protected:
int n_;
};

class Child2;

class Child1 : public Base
{
friend class Outer;
friend class Child2;
public:
Child1(int n) : Base(n) {}
};

class Child2 : public Base
{
friend class Outer;
public:
Child2(int n) : Base(n) {}

Child2(const Child1& rhs) : Base(rhs.n_) {}
};
};

int main()
{
Outer::Child1 c1(4711);
Outer::Child2 c2(c1);
}



regards,

Zeppe
 
J

James Kanze

Hello, consider this program:
class Outer
{
public:
class Base
{
friend class Outer;
public:
Base(int n) : n_(n) {}
protected:
int n_;
};
class Child1 : public Base
{
friend class Outer;
friend class Child2;

The standard is far from clear about this, but the most
reasonable interpretation of the current version is that
this declares (and refers to) a class at namespace scope.
At least, that would seem to be the intent: from a note
(non-normative) in §3.3.1/8 (of the latest draft): "friend
declarations refer to functions or classes that are members
of the nearest enclosing namespace,[...]". The note is,
however, contradicted in §11.4/11, where it says: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 the first sentence of
the paragraph starts "If a friend declaration appears in a
local class[...]" and this "if" seems to be meant to apply
to the entire paragraph, and not just the first sentence.

Note that the text concerning friendship is still somewhat
in a state of flux. The next version of the standard will
differ considerably from the current one, and may differ
from the text in the latest draft as well. Also: since the
text is excedingly unclear, and in a state of flux,
compilers will vary greatly with regards to what they
actually do.
parent[(*this).header] = y; // Update root.
else if (x == left[parent[x]])
left[parent[x]] = y;
else
right[parent[x]] = y;

public:
Child1(int n) : Base(n) {}
};
class Child2 : public Base

Note that according to the above interpretation, this is
*not* the class that Child1 declared as a friend.
{
friend class Outer;
public:
Child2(int n) : Base(n) {}

Child2(const Child1& rhs) : Base(rhs.n_) {}
};
};
int main()
{
Outer::Child1 c1(4711);
Outer::Child2 c2(c1);
}
It compiles with MSVC++ 8.0 SP1 and gcc 4.2.0. It does not compile
with gcc 3.4.4:
$ g++ -Wall -Wextra -std=c++98 -pedantic -g foo.cpp -o runme.exe
foo.cpp: In constructor `Outer::Child2::Child2(const Outer::Child1&)':
foo.cpp:10: error: `int Outer::Base::n_' is protected
foo.cpp:27: error: within this context

Also, worth mentioning is that gcc 4.2.0 doesn't need quite as many
friend declarations as MSVC++ 8.0 SP1 does...

It's possible the g++ is already implementing parts of the
newer standard, which will (I think) make member classes
implicitly friends (or more precisely, a member class is a
member of the enclosing class, and thus has access to
private and protected members of that class).

Note that I've not followed this evolution very closely,
and I could be wrong with regards to many of the details.
The important point to keep in mind, I think, is that it
isn't clear, it is evolving, and that compilers will vary
enormously. About the one point that does seem clear (at
least to me) is that the compiler does do name lookup as if
the friend specifier was absent, and IF it finds the name,
that is what is being declared friend. Thus, in the above
case, you might start with:

class Outer
{
public:
class Base ;
class Child1 ;
class Child2 ;

// ...
} ;

This ensures that any later references to Outer::Base,
Outer::Child1 or Outer::Child2 will find the correct class
(and be legal---if qualified name look-up fails, I think it
is an error).
is this code valid according to the standard and is it
being rejected by the older gcc because of a bug or a
failure to follow the standard as closely as gcc 4.2.0?

I think part of the motivation for the rewrite in the
standard is that interpreted literally, the current standard
makes it impossible to declare a nested class friend. This
was never the intent, and as far as I know, was never
actually implemented by any compiler. What compilers did
implement, however, varied, and will continue to vary, at
least until the next version of the standard is released and
compilers implement it.

If you want to really know what is being planned, and how
this is evolving, I'd suggest asking the question in
comp.std.c++. Be aware, however, that this won't
necessarily help you now, with the compilers you have today.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top