[英]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
一旦执行初始化,就认为该对象已初始化。
请注意,同样没有提到构造函数
我们将主要解释列出初始化内容的含义,因为这是手头的问题。
发生列表初始化时,将按顺序考虑以下内容
聚合类型定义为[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.