3

为什么当我用大括号初始化 std::vector 时

std::vector<TS> vec {ts1, ts2};

编译器调用两次复制构造函数运算符?另一方面 - 使用 push_back 它只调用一次。

#include <iostream>
#include <vector>
using namespace std;

struct TS{
    TS(){
        cout<<"default constructor\n";
    }

    TS(const TS &other) {
        cout<<"Copy constructor\n";
    }

    TS(TS &&other) noexcept{
        cout<<"Move constructor\n";
    }

    TS& operator=(TS const& other)
    {
        cout<<"Copy assigment\n";
        return *this;
    }

    TS& operator=(TS const&& other) noexcept
    {
        cout<<"Move assigment\n";
        return *this;
    }

    ~TS(){
        cout<<"destructor\n";
    }

};

int main() {
    TS ts1;
    TS ts2;
    cout<<"-----------------------------------------\n";
    std::vector<TS> vec {ts1, ts2};
    //vec.push_back(ts1);
    //vec = {ts1, ts2};
    cout<<"-----------------------------------------\n";



    return 0;
}

http://ideone.com/qcPG7X

4

2 回答 2

3

据我了解,initializer_lists 通过 const-reference 传递所有内容。从一个开始可能不安全move。a的initializer_list构造函数vector将复制每个元素。

以下是一些链接: initializer_list 和移动语义

不,这不会按预期工作;你仍然会得到副本。我对此感到非常惊讶,因为我认为 initializer_list 的存在是为了保留一组临时对象,直到它们被移动。

初始化器列表的开始和结束返回 const T *,因此代码中移动的结果是 T const && — 一个不可变的右值引用。这样的表达不能有意义地移开。它将绑定到 T const & 类型的函数参数,因为右值确实绑定到 const 左值引用,并且您仍然会看到复制语义。

移动初始化列表的元素是否安全?

initializer_list 仅提供对其元素的 const 访问。您可以使用 const_cast 来编译该代码,但是这些移动最终可能会导致未定义的行为(如果 initializer_list 的元素确实是 const)。所以,不,这样做是不安全的。如果您确实需要,有解决方法。

我可以列出初始化只移动类型的向量吗?

18.9 的概要清楚地表明,初始化列表的元素总是通过 const-reference 传递。不幸的是,在当前版本的语言中,似乎没有任何方法可以在初始化列表元素中使用移动语义。

关于 std::initializer_list 设计的问题

从 C++ 标准的第 18.9 节:

initializer_list 类型的对象提供对 const E 类型的对象数组的访问。 [注意:一对指针或一个指针加上一个长度将是 initializer_list 的明显表示。initializer_list 用于实现 8.5.4 中指定的初始化列表。复制初始化列表不会复制底层元素。——尾注]

我认为大多数这些事情的原因是 std::initializer_list 实际上不是一个容器。它没有值语义,它有指针语义。引用的最后一部分很明显:复制初始化列表不会复制底层元素。鉴于它们仅用于初始化事物,我认为您没有获得更健壮的容器(例如元组)的所有细节并不令人惊讶。

如果我正确理解最后一部分,则意味着需要两组副本,因为initializer_list不会复制底层元素。(仅当您尝试使用 aninitializer_list而不复制元素时,前面的引用才有意义。)

std::initializer_list 的底层结构是什么?

不,您不能从 initializer_list 的元素中移动,因为 initializer_list 的元素应该是不可变的(参见上面引用的段落的第一句)。这也是为什么只有 const 限定的成员函数才能让您访问元素的原因。


如果你愿意,你可以使用emplace_back

vec.emplace_back(TS());
vec.emplace_back(TS());
vec.push_back(std::move(ts1));
vec.push_back(std::move(ts2));
于 2013-12-10T18:04:12.397 回答
1

因为那里有两个元素。

于 2013-12-10T18:23:18.087 回答