首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >初始化程序列表中的复制构造

初始化程序列表中的复制构造
EN

Stack Overflow用户
提问于 2019-02-02 23:41:23
回答 2查看 1.7K关注 0票数 8

我在探索丑陋的std::intializer_list世界。

据我所知,从标准来看:

第11.6.4节

  1. 类型std::initializer_list的对象是从初始化程序列表构造的,好像实现生成并物化了类型“N const E的数组”的prvalue,其中N是初始化程序列表中的元素数。该数组的每个元素都是用初始化程序列表的对应元素进行复制初始化的,std::initializer_list对象的构造是为了引用该数组。注意:构造函数或为复制选择的转换函数应可在初始化程序列表的上下文中访问(第14条)。-尾注

因此,如果E类型是一个类,我希望调用复制构造函数。

以下类不允许复制构造:

代码语言:javascript
复制
struct NonCopyable {
  NonCopyable() = default;   
  NonCopyable(const NonCopyable&) = delete;
};

我将尝试用这个类实例化一个std::initializer_list

代码语言:javascript
复制
#include <vector>

void foo() {
  std::vector<NonCopyable>{NonCopyable{}, NonCopyable{}};
}

使用g++-8.2 -std=c++14,我得到了我所期望的,编译器错误:

error: use of deleted function 'NonCopyable::NonCopyable(const NonCopyable&)'

太棒了!

然而,行为随着新标准的变化而变化。

实际上,g++-8.2 -std=c++17编译。

编译器资源管理器测试

我认为这是因为新标准最初对copy elision提出了新的要求。

但是,更改标准库实现(保持c++17)返回错误:

clang-7 -std=c++17 -stdlib=libc++失败:

'NonCopyable' has been explicitly marked deleted here NonCopyable(const NonCopyable&) = delete;

编译器资源管理器测试

那我错过了什么?

1) C++17在initializer_list元素的复制构造中是否需要复制?

2)为什么libc++实现没有在这里编译?

编辑请注意,在示例g++ -std=c++17 (编译)中,如果我将默认构造函数更改为“用户定义的”:

代码语言:javascript
复制
struct NonCopyable {
  NonCopyable();
  NonCopyable(const NonCopyable&) = delete;
};

该程序不再编译(不是因为链接错误)。

编译器资源管理器示例

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-02-03 00:05:00

问题是,这种类型:

代码语言:javascript
复制
struct NonCopyable {
  NonCopyable() = default;   
  NonCopyable(const NonCopyable&) = delete;
};

都是可以抄袭的。因此,作为一种优化,由于std::initializer_list只是由一个数组支持,libstdc++所做的只是将整个内容作为一个优化记录到vector中。请注意,即使它有一个已删除的复制构造函数,这种类型也是可以复制的!

这就是为什么当您让用户提供默认构造函数(只编写;而不是= default;)时,突然不再编译了。这使得类型不再是琐碎的可复制的,因此memcpy路径就消失了。

至于这种行为是否正确,我不确定(我怀疑这段代码不能编译吗?)我提交了89164,以防万一)。当然,您希望libstdc++在一些可复制的情况下走上这条路--但也许它需要排除这种情况?在任何情况下,您都可以通过另外删除复制赋值操作符来完成同样的任务(您可能无论如何都想要这样做)--这也会导致类型在复制上是不可复制的。

这没有在C++14中编译,因为您无法构造std::initializer_list -复制初始化,因此需要复制构造函数。但是在保证复制的C++17中,std::initializer_list的构造是很好的。但是,实际构建vector的问题与std::initializer_list完全不同(实际上,这完全是一种“红鲱鱼”)。考虑:

代码语言:javascript
复制
void foo(NonCopyable const* f, NonCopyable const* l) {
  std::vector<NonCopyable>(f, l);
}

在C++11中编译得很好.至少自gcc 4.9以来。

票数 5
EN

Stack Overflow用户

发布于 2019-02-03 00:02:32

C++17在initializer_list元素的复制构造中是否需要复制?

初始化initializer_list的元素从来不保证使用“复制构造”。它只是执行副本初始化。副本初始化是否调用复制构造函数完全取决于初始化过程中发生了什么。

如果您有一个可从int转换的类型,并且执行Type i = 5;,那就是复制初始化。但是它不会调用复制构造函数;它将调用Type(int)构造函数。

是的,initializer_list引用的数组元素的构造容易受到复制省略的影响。包括C++17的保证省略规则。

尽管如此,不容易受到这些规则影响的是vector本身的初始化。vector必须从initializer_list复制对象,因此它们必须有一个可访问的复制构造函数。编译器/库实现如何设法绕过这一点尚不清楚,但这绝对是不规范的行为。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54498610

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档