Reputation: 1569
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:
what is the reasoning behind compiler letting me call func()?
if compiler lets me call func(), why I cannot call ctor?
if I cannot call ctor, how come emplace_back is able to do it?
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
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
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
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
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