繁体   English   中英

具有支撑初始化列表的已删除构造函数的默认构造

[英]Default construction of deleted constructor with braced initializer list

假设我想禁用类的构造,那么我可以执行以下操作(根据最佳样式删除所有构造函数(或其他函数)? ):

// This results in Example being CopyConstructible:
struct Example {
  Example() = delete;
};

要么

struct Example {
  template <typename... Ts>
  Example(Ts&&...) = delete;
};

要么

struct Example {
  Example(const Example&) = delete;
};

如果构造了第一个例子仍然可以被复制(意图是禁用),但是后两个将禁用几乎所有创建Example方法。 如果我使用空的括号初始化列表默认构造上述任何一个,则成功构造实例。 在Meyer的Effective Modern C ++中,他给出了示例(第51页底部):

Widget w3{}; // calls Widget ctor with no args

因此,我希望上述Example类失败,即:

Example e{};

不应该构造,因为它应该调用已删除的默认构造函数。 但是,它确实可用,如果定义为上面的第一种情况,也是可复制的。 查看现场演示 我的问题是:这是正确的,如果是的话,为什么? 另外,如果这是正确的,我如何完全禁用类的销毁?

从头开始

我们将首先阐明初始化对象的含义以及如何/何时/是否调用构造函数。
以下是我的外行人对标准的解释,为简单起见,一些不相关的细节被忽略或破坏。

初始化

初始化程序是以下之一

()     // parentheses
       // nothing
{}     // braced initializer list
= expr // assignment expression

括号和括号初始值设定项列表可能包含更多表达式。
给定struct S ,它们就像这样使用

new S()        // empty parentheses
S s(1, 2)      // parentheses with expression list as (1, 2)
S s            // nothing
S s{}          // empty braced initializer list
S s{{1}, {2}}  // braced initializer list with sublists
S s = 1        // assignment
S s = {1, 2}   // assignment with braced initializer list

请注意,我们还没有提到构造函数

初始化

根据使用的初始化器执行初始化。

new S()        // value-initialize
S s(1, 2)      // direct-initialize
S s            // default-initialize
S s{}          // list-initialize
S s{{1}, {2}}  // list-initialize
S s = 1        // copy-initialize
S s = {1, 2}   // list-initialize

一旦执行初始化,就认为该对象已初始化。

请注意,同样没有提到构造函数

列表初始化

我们将主要解释列出初始化内容的含义,因为这是手头的问题。

发生列表初始化时,将按顺序考虑以下内容

  1. 如果对象是聚合类型,并且列表具有单个元素,该元素是对象的类型或从对象的类型派生,则使用该元素初始化对象
  2. 如果对象是聚合类型,则对象将进行聚合初始化
  3. 如果列表为空,并且对象具有默认构造函数,则对象将进行值初始化(最终调用默认构造函数)
  4. 如果对象是类类型,则考虑构造函数,使用列表的元素执行重载解析

骨料

聚合类型定义为[dcl.init.aggr]

聚合是一个数组或类
- 没有用户提供的,显式的或继承的构造函数
- 没有私有或受保护的非静态数据成员
- 没有虚函数,也没有虚拟,私有或受保护的基类

删除构造函数计入提供构造函数。

聚合的元素定义为

聚合的元素是:
- 对于数组,数组元素按下标顺序递增,或者
- 对于类,声明顺序中的直接基类,后跟声明顺序中不是匿名联合的成员的直接非静态数据成员。

聚合初始化定义为

[...]初始化列表的元素按顺序作为聚合元素的初始值设定项。

Example e{}

遵循上述规则,为什么Example e{}是合法的是因为

the initializer is a braced initializer list
uses list initialization
since Example is an aggregate type
uses aggregate initialization
and therefore does not invoke any constructor

当您编写Example e{} ,它不是默认构造的。 它是聚合初始化的。 所以,是的,它完全没问题。

实际上,以下编译

struct S
{
    S() = delete;
    S(const S&) = delete;
    S(S&&) = delete;
    S& operator=(const S&) = delete;
};

S s{};  //perfectly legal

关闭施工

确保Example不是聚合类型以停止聚合初始化并删除其构造函数。

这通常是微不足道的,因为大多数类都有私有或受保护的数据成员。 因此,经常忘记C ++中存在聚合初始化。

使类非聚合的最简单方法是

struct S
{
    explicit S() = delete;
};
S s{};  //illegal, calls deleted default constructor

但是,截至2017年5月30日,只有gcc 6.1及以上版本和clang 4.0.0将拒绝这一点,所有版本的CL和icc都会错误地接受这一点。

其他初始化

这是在C ++中最疯狂的一个角落,这是 地狱般的 信息看,通过标准来了解到底发生了什么。 已经写了很多参考文献 ,我不会试图解释它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM