12

Sometimes, C++'s notion of privacy just baffles me :-)

class Foo
{
    struct Bar;
    Bar* p;

public:

    Bar* operator->() const
    {
        return p;
    }
};

struct Foo::Bar
{
    void baz()
    {
        std::cout << "inside baz\n";
    }
};

int main()
{
    Foo::Bar b;   // error: 'struct Foo::Bar' is private within this context

    Foo f;
    f->baz();     // fine
}

Since Foo::Bar is private, I cannot declare b in main. Yet I can call methods from Foo::Bar just fine. Why the hell is this allowed? Was that an accident or by design?


Oh wait, it gets better:

Foo f;
auto x = f.operator->();   // :-)
x->baz();

Even though I am not allowed to name the type Foo::Bar, it works just fine with auto...


Noah wrote:

type names defined within a class definition cannot be used outside their class without qualification.

Just for fun, here is how you can get at the type from outside:

#include <type_traits>

const Foo some_foo();

typedef typename std::remove_pointer<decltype( some_foo().operator->() )>::type Foo_Bar;
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 8
    To abbreviate your code example, you're basically asking whether it is legal by design to return a private type (`Foo::Bar*`) from a public member function (`Foo::operator ->()`)? – stakx - no longer contributing Jun 01 '10 at 18:30
  • @stakx Hmmm, I guess so :) +1 – fredoverflow Jun 01 '10 at 18:37
  • Concerning your `auto` code example, I thought that was an abbreviation for `auto int`. Does this actually compile and run correctly? – stakx - no longer contributing Jun 01 '10 at 18:44
  • 2
    You still haven't gotten access to the typename Bar that is defined within Foo. You've invented a new typename that refers to the same type. You're assuming the type's name and the type are the same thing and in C++ they're apparently not...though I've not yet found anything in the standard that explicitly says that. – Edward Strange Jun 01 '10 at 19:24

3 Answers3

6

Trying to find anything in the standard that would spell it out in detail but I can't. The only thing I can find is 9.9:

Type names obey exactly the same scope rules as other names. In particular, type names defined within a class definition cannot be used outside their class without qualification.

Essentially, the name of Foo::Bar is private to Foo, not the definition. Thus you can use Bars outside of Foo, you just can't refer to them by type since that name is private.

The name lookup rules for members would also seem to have some effect on this. I don't see anything that specifically references "nested class" and thus they wouldn't be allowed to (if my lack of finding anything in fact is because it's not there).

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
3

I can't provide a full answer, but maybe a starting point. The C++ 1998 specification includes the following code example under paragraph 11.3 [class.access] (p. 175):

class A
{
    class B { };
public:
    typedef B BB;
};

void f()
{
    A::BB x;   // OK, typedef name A::BB is public
    A::B y;    // access error, A::B is private
}

In this example, a private type is "published" through a public typedef. Although it's not the same thing as publishing a type through a member function signature, it's similar.

stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
  • 2
    The same text is in clause 11, paragraph 4 of the C++0x FCD; I think paragraph 5 explains why you can do it: "It should be noted that it is access to members and base classes that is controlled, not their visibility.Names of members are still visible, and implicit conversions to base classes are still considered, when those members and base classes are inaccessible. The interpretation of a given construct is established without regard to access control. If the interpretation established makes use of inaccessible member names or base classes, the construct is ill-formed." – Niall C. Jun 01 '10 at 18:57
  • *@Niall C.:* I'm not sure that the distinction between accessibility and visibility provides the answer. Visibility doesn't seem an issue, here. And the above code example still makes a private type accessible (in a limited fashion, though). – stakx - no longer contributing Jun 01 '10 at 19:01
  • Even though `Foo::Bar` is inaccessible outside of `Foo`, its members are visible and accessible through the `Bar *` being returned by the `operator->()` overload.. – Niall C. Jun 01 '10 at 19:02
1

I think this is by design. You cannot explicitly create instance of Foo::Bar but it could be returned from member functions and then you could pass it to other member functions. This lets you to hide implementation details of your class.

Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212