gpt4 book ai didi

c++ - 在初始化列表中复制构造

转载 作者:可可西里 更新时间:2023-11-01 18:35:22 28 4
gpt4 key购买 nike

我正在探索 std::intializer_list丑陋世界.

据我对标准的理解:

§ 11.6.4:

  1. An object of type std::initializer_list is constructed from an initializer list as if the implementation generated and materialized (7.4) a prvalue of type “array of N const E”, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list object is constructed to refer to that array. [ Note: A constructor or conversion function selected for the copy shall be accessible (Clause 14) in the context of the initializer list. — end note ] [...]

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


下面的类不允许复制构造:

struct NonCopyable {
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
};

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

#include <vector>

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

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

错误:使用已删除的函数“NonCopyable::NonCopyable(const NonCopyable&)”

完美!


但是,行为随着新标准而改变。

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

Compiler Explorer Test


我以为是因为关于copy elision的新要求首先由新标准提供。

但是,更改标准库实现(保留 c++17)错误又回来了:

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

'NonCopyable' 已在此处明确标记为已删除 NonCopyable(const NonCopyable&) = delete;

Compiler Explorer Test


那我错过了什么?

1) C++17 需要initializer_list 元素的复制构造中进行复制省略吗?

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


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

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

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

Compiler Explorer Example

最佳答案

问题是这种类型:

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 完全不同(事实上,这完全是转移注意力)。考虑:

void foo(NonCopyable const* f, NonCopyable const* l) {
std::vector<NonCopyable>(f, l);
}

在 C++11 中编译得很好……至少从 gcc 4.9 开始是这样。

关于c++ - 在初始化列表中复制构造,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54498610/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com