27
class B {
private:
    friend class C;
    B() = default;
};

class C : public B {};
class D : public B {};

int main() {
    C {};
    D {};
    return 0;
}

我假设因为只有 classC是 的朋友B,并且B的构造函数是私有的,所以只有 classC是有效的并且D不允许实例化B。但这不是它的工作原理。我的推理在哪里错了,以及如何实现对允许哪些类继承某个基类的这种控制?

更新:正如其他人在评论中指出的那样,上面的代码片段在 C++14 下工作,但不是 C++17。将实例化更改为C c; D d;inmain()在 C++17 模式下也可以按预期工作。

4

2 回答 2

24

这是添加到 C++17 的新功能。正在发生的事情C现在被认为是一个聚合。由于它是一个聚合,它不需要构造函数。如果我们查看[dcl.init.aggr]/1我们会发现聚合是

聚合是一个数组或一个类

  • 没有用户提供的、显式的或继承的构造函数([class.ctor]),

  • 没有私有或受保护的非静态数据成员(子句 [class.access]),

  • 没有虚函数,并且

  • 没有虚拟、私有或受保护的基类([class.mi])。

[注意:聚合初始化不允许访问受保护和私有基类的成员或构造函数。——尾注]

我们检查了所有这些要点。您没有在项目符号 1 中声明任何构造函数CD您没有任何数据成员,因此第二个项目符号无关紧要,并且您的基类是公共的,因此满足第三个项目符号。

C++11/14 和 C++17 之间发生的变化是聚合现在可以有基类。你可以在这里看到旧的措辞,它明确指出不允许使用基类。

std::is_aggregate_v我们可以通过检查特征来确认这一点

int main()
{
    std::cout << std::is_aggregate_v<C>;
}

这将打印 1。


请注意,因为C是您的朋友B可以使用

C c{};
C c1;
C c2 = C();
    

作为初始化 a 的有效方法C。因为D不是B唯一有效的朋友,D d{};因为那是聚合初始化。D所有其他形式都尝试默认初始化,但由于删除了默认构造函数,因此无法完成。

于 2019-04-05T12:40:00.113 回答
-5

C++ 中构造函数的默认访问是什么

如果类 X 没有用户声明的构造函数,则没有参数的构造函数被隐式声明为默认构造函数。隐式声明的默认构造函数是其类的内联公共成员。

如果类定义没有显式声明复制构造函数,则隐式声明。[...] 隐式声明的复制/移动构造函数是其类的内联公共成员。

类 C 和 D 的构造函数由编译器在内部生成。

顺便说一句:如果你想玩继承,请确保你定义了虚拟析构函数。

于 2019-04-05T12:16:50.710 回答