beldaz
beldaz

Reputation: 4481

Visibility of privately inherited typedefs to nested classes

In the following example (apologies for the length) I have tried to isolate some unexpected behaviour I've encountered when using nested classes within a class that privately inherits from another. I've often seen statements to the effect that there is nothing special about a nested class compared to an unnested class, but in this example one can see that a nested class (at least according to GCC 4.4) can see the public typedefs of a class that is privately inherited by the closing class.

I appreciate that typdefs are not the same as member data, but I found this behaviour surprising, and I imagine many others would, too. So my question is twofold:

  1. Is this standard behaviour? (a decent explanation of why would be very helpful)
  2. Can one expect it to work on most modern compilers (i.e., how portable is it)?

#include <iostream>

class Base {
  typedef int priv_t;
  priv_t priv;
public:
  typedef int pub_t;
  pub_t pub;
  Base() : priv(0), pub(1) {}
};

class PubDerived : public Base {
public:
  // Not allowed since Base::priv is private
  // void foo() {std::cout << priv << "\n";}

  class Nested {
    // Not allowed since Nested has no access to PubDerived member data
    // void foo() {std::cout << pub << "\n";}

    // Not allowed since typedef Base::priv_t is private
    // void bar() {priv_t x=0; std::cout << x << "\n";}
  };

};

class PrivDerived : private Base {
public:
  // Allowed since Base::pub is public
  void foo() {std::cout << pub << "\n";}

  class Nested {
  public:
    // Works (gcc 4.4 - see below)
    void fred() {pub_t x=0; std::cout << x << "\n";}
  };
};

int main() {

  // Not allowed since typedef Base::priv_t private
  // std::cout << PubDerived::priv_t(0) << "\n";

  // Allowed since typedef Base::pub_t is inaccessible
  std::cout << PubDerived::pub_t(0) << "\n"; // Prints 0

  // Not allowed since typedef Base::pub_t is inaccessible
  //std::cout << PrivDerived::pub_t(0) << "\n";

  // Works (gcc 4.4)
  PrivDerived::Nested o;
  o.fred(); // Prints 0
  return 0;
}

Upvotes: 8

Views: 3645

Answers (5)

catphive
catphive

Reputation: 3541

Short answer: Nested classes have access to the containing classes private member in C++0x, but not C++1998 and C++2003. It is however legal for C++98 and C++2003 compilers support the C++0x behavior, since the old behavior is considered a defect.

In the C++98 and 2003 standards section 11.8.1 stated:

The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed. The members of an enclosing class have no special access to members of a nested class; the usual access rules (clause 11) shall be obeyed.

C++0x section 11.8.1 says:

A nested class is a member and as such has the same access rights as any other member. The members of an enclosing class have no special access to members of a nested class; the usual access rules (Clause 11) shall be obeyed.

Core Language Defect Report 45 shows that the original behavior was considered a defect, so it is legal for non-c++0x compilers to also support the new behavior, although not required.

Upvotes: 2

AnT stands with Russia
AnT stands with Russia

Reputation: 320641

Preface: In the answer below I refer to some differences between C++98 and C++03. However, it turns out that the change I'm talking about haven't made it into the standard yet, so C++03 is not really different from C++98 in that respect (thanks to Johannes for pointing that out). Somehow I was sure I saw it in C++03, but in reality it isn't there. Yet, the issue does indeed exist (see the DR reference in Johannes comment) and some compilers already implement what they probably consider the most reasonable resolution of that issue. So, the references to C++03 in the text below are not correct. Please, interpret the references to C++03 as references to some hypothetical but very likely future specification of this behavior, which some compilers are already trying to implement.


It is important to note that there was a significant change in access rights for nested classes between C++98 and C++03 standards.

In C++98 nested class had no special access rights to the members of enclosing class. It was basically completely independent class, just declared in the scope of the enclosed class. It could only access public members of the enclosing class.

In C++03 nested class was given access rights to the members of the enclosing class as a member of the enclosing class. More precisely, nested class was given the same access rights as a static member function of the enclosing class. I.e. now the nested class can access any members of the enclosing class, including private ones.

For this reason, you might observe the differences between different compilers and versions of the same compiler depending on when they implemented the new specification.

Of course, you have to remember that an object of the nested class is not tied in any way to any specific object of the enclosing class. As far as the actual objects are concerned, these are two independent classes. In order to access the non-static data members or methods of the enclosing class from the nested class you have to have a specific object of the enclosing class. In other words, once again, the nested class indeed behaves as just like a static member function of the enclosing class: it has no specific this pointer for the enclosing class, so it can't access the non-static members of the enclosing class, unless you make an effort to give it a specific object of the enclosing class to access. Without it the nested class can only access typedef-names, enums, and static members of the enclosing class.

A simple example that illustrates the difference between C++98 and C++03 might look as follows

class E {
  enum Foo { A };
public:
  enum Bar { B };

  class I {
    Foo i; // OK in C++03, error in C++98
    Bar j; // OK in C++03, OK in C++98
  };
};

This change is exactly what allows your PrivDerived::Nested::fred function to compile. It wouldn't pass compilation in a pedantic C++98 compiler.

Upvotes: 4

p00ya
p00ya

Reputation: 3709

I've done my best to assemble all the relevant clauses from the ISO/IEC 14882:1997.

Section 9.7:

A class defined within another is called a nested class. The name of a nested class is local to its enclosing class. The nested class is in the scope of its enclosing class. Except by using explicit pointers, references, and object names, declarations in a nested class can use only type names, static members, and enumerators from the enclosing class.

11.2.1 (should be fairly obvious):

[...] If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class.

9.9 Nested Type Names:

Type names obey exactly the same scope rules as other names.

Then in 11.8:

The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (11) shall be obeyed. The members of an enclosing class have no special access to members of a nested class; the usual access rules (11) shall be obeyed.

From which I conclude that the behaviour you're experiencing is non-standard. The nested class shouldn't have any 'special' access to the private member of the base.

However, Comeau C++, which seems to have the best standard support, has the same behaviour as GCC (allows fred, disallows bar with "error: type "Base::priv_t" (declared at line 4) is inaccessible").

Upvotes: 1

dirkgently
dirkgently

Reputation: 111210

As per the standard:

9.2 Class members

1 [...] Members of a class are data members, member functions (9.3), nested types, and enumerators. Data members and member functions are static or non-static; see 9.4.Nested types are classes (9.1, 9.7) and enumerations (7.2) defined in the class, and arbitrary types declared as members by use of a typedef declaration (7.1.3).

To answer your questions:

  1. Is this standard behaviour? (a decent explanation of why would be very helpful)

No. At least with the typedefs not being accessible. However, note that:

class Nested {
    // Not allowed since Nested has no access to PubDerived member data
     void foo() {std::cout << pub << "\n";}

is problematic. The nested class neither has an instance of PubDerived to work with nor is the pub a static member object.

  1. Can one expect it to work on most modern compilers (i.e., how portable is it)?

Yes. But do check the documentation for standards compliance. And always: Try out with a few compilers such as Comeau in strict mode.

Upvotes: 1

fbrereto
fbrereto

Reputation: 35935

This doesn't answer your question, but according to my reading of the C++ FAQ Lite 24.6 what you are trying to do isn't allowed. I'm not sure why gcc is permitting it; have you tried it in other compilers as well?

Upvotes: 0

Related Questions