|
|
 |
 |
 |
 |
Problems with protected variables despite friends
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
Eric Lilja wrote: > 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
On 30 Maj, 14:52, Zeppe <zep_p@.remove.all.this.long.comment.yahoo.it> wrote:
> Eric Lilja wrote: > > 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
That doesn't help at all and I think the friend declaration is a forward declaration in itself.
Eric Lilja wrote: > On 30 Maj, 14:52, Zeppe <zep_p@.remove.all.this.long.comment.yahoo.it> > wrote: >> 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: > 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
On May 30, 1:35 pm, Eric Lilja <mindcoo@gmail.com> wrote:
> 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. -- James Kanze (GABI Software) email:james.ka@gmail.com Conseils en informatique oriente objet/ Beratung in objektorientierter Datenverarbeitung 9 place Smard, 78210 St.-Cyr-l'cole, France, +33 (0)1 30 23 00 34
|
 |
 |
 |
 |
|