在VS2012,VS2017和https://www.onlinegdb.com/online_c++_compiler上检查了这种奇怪的编译行为
对于私有嵌套类,您可以在外部调用公共函数,但不能调用公共构造函数。
3个问题:
编译器让我调用func()的原因是什么?
如果编译器让我调用func(),为什么我不能调用ctor?
如果我不能打电话给ctor,emplace_back怎么能这样做呢?
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?
}
正如我所见,我仍然可以创建一个(静态)函数createObject():
class Outer {
struct PrivateInner {
PrivateInner() {}
static PrivateInner createObject() { return PrivateInner(); }
void func() {}
};
.....
};
然后调用它。
如果从实例调用static不是纯粹的标准事物,那么createObject()可能是非静态的。
c.innerVect.push_back(c.inner.createObject()); // COMPILING
to&#34; hack&#34;汇编
答案 0 :(得分:4)
请注意,嵌套的struct
PrivateInner
声明为private
,因此只有Outer::PrivateInner
为private
,您无法使用此名称声明变量如Outer::PrivateInner pi;
没有足够的访问权限,但您可以像decltype(Outer::inner) pi;
一样编写它。
另一方面,它的构造函数和成员函数是public
,因此可以调用它们。
c.inner.func(); // COMPILING, but why?
func()
为public
,因此如果您有Outer::PrivateInner
类型的实例,则可以在其上调用func
。
c.innerVect.push_back(Outer::PrivateInner()); // NOT COMPILING, why no access to ctor if there is access to func()?
它与构造函数无关,你不能在这里使用名称Outer::PrivateInner
。
c.innerVect.emplace_back(); // COMPILING, but why? Doesn't it call Outer::PrivateInner inside?
构造函数是public
,然后它可以用来构造对象。 std::vector
不直接使用Outer::PrivateInner
之类的名称;它使用指定的名称作为模板参数。
c.innerVect.push_back(c.inner.createObject()); // COMPILING
但c.innerVect.push_back(Outer::PrivateInner::createObject());
无法编译。
答案 1 :(得分:4)
访问控制适用于名称。这意味着只限制struct
PrivateInner
的名称。 struct
本身的成员拥有自己的访问控制权。因此PrivateInner
的所有成员都是公开的。
11会员访问控制[class.access]
1班级成员可以
- 的私有强>;也就是说,它的名称只能由声明它的类的成员和朋友使用。
...
4访问控制统一应用于所有名称,无论名称是什么 从声明或表达中引用。 ...
正如您已经发现的那样,如果您没有使用其名称,则可以使用构造函数(以及struct
):
c.innerVect.push_back({});
c.innerVect.emplace_back();
甚至
template <typename T>
T createPrivateInner()
{
return T();
}
...
c.innerVect.push_back(createPrivateInner<decltype(Outer::inner)>());
答案 2 :(得分:3)
该成员是公开的,因此您当然可以调用其成员函数。如果其名称可见,那么其公共成员也是如此。
同样,内部类是私有的,所以当然你不能从类外面引用它的名字。这些是访问控制规则的基本定义。
emplace_back
可以调用构造函数,因为std::vector
从允许引用它的人那里收到了类型作为模板参数。实例化模板时发生访问检查。那时它的名字是可以访问的。
您可以使用decltype
在任何地方调用构造函数:
Outer c;
auto this_works_too = decltype(c.inner){};
这是有效的,因为您不必通过无法访问的名称来引用它。
答案 3 :(得分:0)
成员“createObject()”是私有的。所以,当然,你无法访问它。您应该在公共字段中添加一些成员函数来实现此私有成员。