C++对不同功能的私有嵌套访问

C++ private nested access to different functions

本文关键字:嵌套 访问 功能 C++      更新时间:2023-10-16

发现这种奇怪的编译行为,在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() {}
    };
.....
};

然后调用它。

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

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

"破解"编译

注意嵌套的struct PrivateInner被声明为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());不会编译。

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

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

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

您可以使用 decltype 在类外部的任何地方调用构造函数:

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

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

访问控制应用于名称。这意味着只有struct PrivateInner的名称受到限制。struct本身的成员有自己的访问控制。所以PrivateInner的所有成员都是公开的。

11 成员访问控制 [类访问]

1 类的成员可以是

  • private;也就是说,它的名称只能由声明它的类的成员和朋友使用。

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)>());

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