C++ private nested class - 访问不同的函数

C++ private nested class - access to different functions

发现了这个奇怪的编译行为,检查了 VS2012、VS2017 和 https://www.onlinegdb.com/online_c++_compiler)

基本上,对于私有嵌套 类,您可以在外部调用 public 函数,但不能调用 public 构造函数。

3 个问题:


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() {}
    };
.....
};

然后调用它。

如果从实例调用静态不是纯粹的标准事物,则 createObject() 可能是非静态的。

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

到"hack"编译

成员是public当然可以调用它的成员函数。如果它的名字可见,那么它自己的 public 成员也是如此。

同样,内部 class 是私有的,因此您当然不能从 class 外部引用它的名称。这些是访问控制规则的基本定义。

emplace_back 可以调用构造函数,因为 std::vector 从允许引用它的人那里接收到类型作为模板参数。实例化模板时发生访问检查。那时它的名字是可以访问的。

您可以在 class 之外的任何地方使用 decltype:

调用构造函数
Outer c;
auto this_works_too = decltype(c.inner){};

之所以有效,是因为您不必使用无法访问的名称来引用它。

成员"createObject()"是私有的。因此,您当然无法访问它。您应该在 public 字段中添加一些成员函数来实现此私有成员。

注意嵌套的structPrivateInner声明为private,所以只有Outer::PrivateInnerprivate,不能用这个名字来在没有足够访问权限的情况下像 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()); 无法编译。

访问控制应用于名称。这意味着只有 struct PrivateInner 的名称受到限制。 struct 本身的成员有自己的访问控制。所以 PrivateInner 的所有成员都是 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. ...

正如您已经发现的那样,如果您不使用其名称,则可以使用构造函数(以及一般的 struct):

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

甚至

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

...

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