C++11 标准是否要求实现者优先考虑 std::vector 的 noexcept 移动构造函数而不是 const 复制构造函数?

Does C++11 standard require implementers to prioritize noexcept move constructor over const copy constructor for std::vector?

提问人:Tadeus Prastowo 提问时间:9/25/2017 最后编辑:Tadeus Prastowo 更新时间:9/26/2017 访问量:169

问:

阅读这个这个以及标准的 23.3.6.5/1,在最新的 C++ 标准草案中,当作为操作的结果重新分配其元素时,实现者应该优先使用非抛出移动构造函数而不是常量复制构造函数?是 13.3.3.1.4/1 关于引用绑定的重载分辨率吗?T(T &&t) noexceptT(const T &t)std::vector<T>push_back

编辑 1

我在 13.3.3.1.4/1 上争论,原因如下:

  • 13.3/2

    重载解析选择要在语言中的七个不同上下文中调用的函数。[...]调用构造函数以直接初始化类对象 (8.5) (13.3.1.3);[...]这些上下文中的每一个都以自己独特的方式定义候选函数集和参数列表。但是,一旦确定了候选函数和参数列表,最佳函数的选择在所有情况下都是相同的:首先,选择候选函数的子集(具有适当数量的参数并满足某些其他条件的函数)以形成一组可行的函数(13.3.2)。然后,根据将每个参数与每个可行函数的相应参数匹配所需的隐式转换序列 (13.3.3.1) 选择最佳可行函数。

  • 13.3.2/1

    从为给定上下文构造的候选函数集 (13.3.1) 中,选择一组可行函数,通过比较参数转换序列以获得最佳拟合 (13.3.3),从中选择最佳函数。可行函数的选择考虑了参数和函数参数之间的关系,而不是转换序列的排名。

  • 13.3.1.3/1

    当类类型的对象被直接初始化 (8.5) 或从相同或派生类类型的表达式 (8.5) 复制初始化时,重载解析会选择构造函数

  • 13.3.3.1/5

    有关参数类型为引用的情况,请参见 13.3.3.1.4

  • 13.3.3.1.4/1

    当引用类型的参数直接绑定 (8.5.3) 到参数表达式时,隐式转换序列是身份转换,[...]

因此,我得出结论,身份转换的要求导致需要优先于 .但是,与我争论的另一方并不相信。所以,我在这里问。T(T &&t) noexceptT(const T &t)

编辑 2

以下是 23.3.6.513.3.3.1.4 之间的链接:

首先,23.3.6.5 要求满足以下条件:std::vector

[...] 备注:[...]如果不是由 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符或任何 InputIterator 操作引发异常,则不会产生任何影响。[...]void push_back(const T& x);void push_back(T&& x);

23.3.6.1/2 涵盖了这一点,它需要满足以下条件:

向量满足容器和可逆容器的所有要求(在 23.2 的两个表格中给出),[...]

也就是说,应符合 23.2.1/7 的规定,其中规定了以下内容:std::vector

除非另有说明,否则本子句中定义的所有容器都使用分配器获取内存(参见 17.6.3.5)。

以及 23.2.1/3 中指定以下内容:

对于受本子句影响的声明 的组件,存储在这些组件中的对象应使用 allocator_traits<allocator_type>::construct 函数构造,并使用 allocator_traits<allocator_type>::d estroy 函数 (20.7.8.2) 销毁。仅为容器的元素类型调用这些函数,而不为容器使用的内部类型调用这些函数。allocator_type

由于 20.7.8.2 仅指定一个函数,如下所示:construct

template <class T, class... Args>
static void construct(Alloc& a, T* p, Args&&... args);

20.7.8.2/5 要求满足以下条件:

效果:如果调用格式正确,则调用;否则,调用 ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)。a.construct(p, std::forward<Args>(args)...)

以及 20.7.9.2/12 要求默认分配器的以下内容:

效果: ::new((void *)p) U(std::forward<Args>(args)...)

在 20.7.8.2/520.7.9.2/12 中都将遵循 13.3.3.1.4/1 中的构造函数重载解析要求。T(std::forward<Args>(args)...)U(std::forward<Args>(args)...)

因此,该标准要求实现者在因操作而重新分配其元素时,优先使用 move-constructor 而不是 copy-constructor。T(T &&t) noexceptT(const T &t)std::vector<T>push_back

C++ C++11 矢量 复制构造函数 move-constructor

评论

1赞 AndyG 9/26/2017
不幸的是,我认为答案是否定的。由实施者决定该怎么做;包括纯副本。对于不可复制的可插入类型,当然需要移动。
0赞 Tadeus Prastowo 9/26/2017
@AndyG:为什么 13.3.3.1.4/1 没有义务将 move-constructor 优先于 copy-constructor?
3赞 Passer By 9/26/2017
您可能希望在此处粘贴相关文本。关注 5 个链接并不是那么有趣
1赞 Igor Tandetnik 9/26/2017
由于它使用完美转发,因此可能会被诱骗调用副本或移动构造函数。这完全取决于是传递左值还是右值引用。这就是完美转发的意义所在。请注意,它不是右值引用 - 它是一个转发引用,可以以任何一种方式绑定。construct(Alloc& a, T* p, Args&&... args)argsArgs&&
1赞 Tadeus Prastowo 9/26/2017
@AndyG 您的评论给了我一个想法,即通过删除我的 const 复制构造函数并使用只需要 .std::vectorMoveInsertable

答: 暂无答案