Alek86
Alek86

Reputation: 1569

C++ private nested class - access to different functions

Found this strange compilation behavior, checked on VS2012, VS2017 and https://www.onlinegdb.com/online_c++_compiler)

Basically for private nested classes you can call public functions outside, but not public constructors.

3 questions:


class Outer {
    struct PrivateInner {
        PrivateInner() {}
        void func() {}
    };
public:
    PrivateInner inner;
    std::vector<PrivateInner> innerVect;
};

void f1()
{
    Outer c;
    c.inner.func(); // COMPILING, but why?
}

void f2()
{
    Outer c;
    c.innerVect.push_back(Outer::PrivateInner()); // NOT COMPILING, why no access to ctor if there is access to func()?
    c.innerVect.emplace_back(); // COMPILING, but why? Doesn't it call Outer::PrivateInner inside?
}

As I see I still can create a (static) function createObject():

class Outer {
    struct PrivateInner {
        PrivateInner() {}
        static PrivateInner createObject() { return PrivateInner(); }
        void func() {}
    };
.....
};

and then call it.

createObject() may be non-static if calling static from instances is not pure standard thing.

c.innerVect.push_back(c.inner.createObject()); // COMPILING

to "hack" compilation

Upvotes: 11

Views: 13430

Answers (4)

songyuanyao
songyuanyao

Reputation: 172924

Note that the nested struct PrivateInner is declared as private, so only Outer::PrivateInner is private, you can't use this name to declare variable like Outer::PrivateInner pi; without sufficient access right, but you can write it like decltype(Outer::inner) pi;.

On the other hand, its constructor and member function are public, so they can be called.

c.inner.func(); // COMPILING, but why?

func() is public, so if you have got an instance of type Outer::PrivateInner you can call func on it.

c.innerVect.push_back(Outer::PrivateInner()); // NOT COMPILING, why no access to ctor if there is access to func()?

It has nothing to do with the constructor, you just can't use the name Outer::PrivateInner here.

c.innerVect.emplace_back(); // COMPILING, but why? Doesn't it call Outer::PrivateInner inside?

The constructor is public, then it could be used to construct the object. std::vector doesn't use the name like Outer::PrivateInner directly; it uses the name specified as the template argument.

BTW: For the same reason,

c.innerVect.push_back(c.inner.createObject()); // COMPILING

but c.innerVect.push_back(Outer::PrivateInner::createObject()); won't compile.

Upvotes: 12

Mihayl
Mihayl

Reputation: 3911

The access control is applied to names. That means only the name of the struct PrivateInner is restricted. The members of the struct itself have own access control. So all members of PrivateInner are public.

11 Member access control [class.access]

1 A member of a class can be

  • private; that is, its name can be used only by members and friends of the class in which it is declared.

...

4 Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions. ...

As you already found out, you can use the constructor (and the struct in general) if you're not using its name:

c.innerVect.push_back({});
c.innerVect.emplace_back();

even

template <typename T>
T createPrivateInner()
{
    return T();
}

...

c.innerVect.push_back(createPrivateInner<decltype(Outer::inner)>());

Upvotes: 4

Tim Seguine
Tim Seguine

Reputation: 2914

The member is public so of course you can call its member functions. If its name is visible, then so are its own public members.

Likewise the inner class is private, so of course you can't refer to its name from outside the class. These are the basic definitions of the access control rules.

emplace_back can call the constructor because std::vector received the type as a template parameter from someone who was allowed to refer to it. The access check occurred when the template was instantiated. At that point its name was accessible.

You can call the constructor outside of the class anywhere using decltype:

Outer c;
auto this_works_too = decltype(c.inner){};

This works because you don't have to refer to it by an inaccessible name.

Upvotes: 4

Wudi Zheng
Wudi Zheng

Reputation: 1

The member "createObject()" is private. So, of course, you cannot access it. You should add some member function in public field to implement this private member.

Upvotes: 0

Related Questions