简体   繁体   English

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

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

Let's say that I want to disable the construction of class, then I can do the following (as per Best style for deleting all constructors (or other function)? ): 假设我想禁用类的构造,那么我可以执行以下操作(根据最佳样式删除所有构造函数(或其他函数)? ):

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

or 要么

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

or 要么

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

where the first example can still be copied if constructed (which the intention is to disable), but the second two will disable almost all methods for creating Example . 如果构造了第一个例子仍然可以被复制(意图是禁用),但是后两个将禁用几乎所有创建Example方法。 If I default construct any of the above using an empty braced initializer list, then the instance is successfully constructed. 如果我使用空的括号初始化列表默认构造上述任何一个,则成功构造实例。 In Meyer's Effective Modern C++ he gives the example (bottom of page 51): 在Meyer的Effective Modern C ++中,他给出了示例(第51页底部):

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

which I would thus expect to fail for the above Example classes ie: 因此,我希望上述Example类失败,即:

Example e{};

should not construct since it should call the deleted default constructor. 不应该构造,因为它应该调用已删除的默认构造函数。 However, it does, is usable, and if defined as the first case above, is also copyable. 但是,它确实可用,如果定义为上面的第一种情况,也是可复制的。 See live demo . 查看现场演示 My question is: Is this correct, and if so, why? 我的问题是:这是正确的,如果是的话,为什么? Also, if this is correct, how do I completely disable the destruction of the class? 另外,如果这是正确的,我如何完全禁用类的销毁?

From ground up 从头开始

We will first clarify what it means to initialize an object and how/when/if a constructor is invoked. 我们将首先阐明初始化对象的含义以及如何/何时/是否调用构造函数。
The following is my laymen's interpretation of the standard, for simplicity's sake some irrelevant details have been omitted or mangled. 以下是我的外行人对标准的解释,为简单起见,一些不相关的细节被忽略或破坏。

Initializer 初始化

An initializer is one of the following 初始化程序是以下之一

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

The parentheses and braced initializer list may contain further expressions. 括号和括号初始值设定项列表可能包含更多表达式。
They are used like this, given struct S 给定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

Note that we have not yet mentioned constructors 请注意,我们还没有提到构造函数

Initialization 初始化

Initialization is performed according to what initializers are used. 根据使用的初始化器执行初始化。

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

Once initialization is performed, the object is considered initialized. 一旦执行初始化,就认为该对象已初始化。

Note that, again, constructors have not been mentioned 请注意,同样没有提到构造函数

List initialize 列表初始化

We will be primarily explaining what it means to list initialize something, as this is the question at hand. 我们将主要解释列出初始化内容的含义,因为这是手头的问题。

When list initialization occurs, the following is considered in order 发生列表初始化时,将按顺序考虑以下内容

  1. If the object is an aggregate type and the list has a single element that is the object's type or is derived from the object's type, the object is initialized with that element 如果对象是聚合类型,并且列表具有单个元素,该元素是对象的类型或从对象的类型派生,则使用该元素初始化对象
  2. If the object is an aggregate type, the object is aggregate initialized 如果对象是聚合类型,则对象将进行聚合初始化
  3. If the list is empty, and the object has a default constructor, the object is value-initialized (ends up calling default constructor) 如果列表为空,并且对象具有默认构造函数,则对象将进行值初始化(最终调用默认构造函数)
  4. If the object is a class type, the constructors are considered, performing overload resolution with the elements of the list 如果对象是类类型,则考虑构造函数,使用列表的元素执行重载解析

Aggregate 骨料

An aggregate type is defined as [dcl.init.aggr] 聚合类型定义为[dcl.init.aggr]

An aggregate is an array or a class with 聚合是一个数组或类
-- no user-provided, explicit, or inherited constructors - 没有用户提供的,显式的或继承的构造函数
-- no private or protected non-static data members - 没有私有或受保护的非静态数据成员
-- no virtual functions, and no virtual, private, or protected base classes - 没有虚函数,也没有虚拟,私有或受保护的基类

Having a deleted constructor does not count towards providing a constructor. 删除构造函数计入提供构造函数。

Elements of an aggregate is defined as 聚合的元素定义为

The elements of an aggregate are: 聚合的元素是:
-- for an array, the array elements in increasing subscript order, or - 对于数组,数组元素按下标顺序递增,或者
-- for a class, the direct base classes in declaration order, followed by the direct non-static data members that are not members of an anonymous union, in declaration order. - 对于类,声明顺序中的直接基类,后跟声明顺序中不是匿名联合的成员的直接非静态数据成员。

Aggregate-initialization is defined as 聚合初始化定义为

[...] the elements of the initializer list are taken as initializers for the elements of the aggregate, in order. [...]初始化列表的元素按顺序作为聚合元素的初始值设定项。

Example e{}

Following the rules above the question why Example e{} is legal is because 遵循上述规则,为什么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

When you write Example e{} , it is not default constructed. 当您编写Example e{} ,它不是默认构造的。 It is aggregate initialized. 它是聚合初始化的。 So, yes it is perfectly fine. 所以,是的,它完全没问题。

In fact, the following compiles 实际上,以下编译

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

S s{};  //perfectly legal

Turn off construction 关闭施工

Make sure that Example is not an aggregate type to stop aggregate initialization and delete its constructors. 确保Example不是聚合类型以停止聚合初始化并删除其构造函数。

This is often trivial as most classes have private or protected data members. 这通常是微不足道的,因为大多数类都有私有或受保护的数据成员。 As such, it is often forgotten that aggregate initialization exists in C++. 因此,经常忘记C ++中存在聚合初始化。

The simplest way to make a class non-aggregate would be 使类非聚合的最简单方法是

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

However, as of 2017 May 30, only gcc 6.1 and above and clang 4.0.0 will reject this, all versions of CL and icc will incorrectly accept this. 但是,截至2017年5月30日,只有gcc 6.1及以上版本和clang 4.0.0将拒绝这一点,所有版本的CL和icc都会错误地接受这一点。

Other initializations 其他初始化

This is one of the craziest corners in C++, and it was hellish informative to look through the standard to understand what exactly happened. 这是在C ++中最疯狂的一个角落,这是 地狱般的 信息看,通过标准来了解到底发生了什么。 There have been lots of references already written and I will not attempt to explain them. 已经写了很多参考文献 ,我不会试图解释它们。

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

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