使用初始值设定项列表构造的向量中的复制行为

May*_*ybe 0 c++ vector

我意识到使用initializer_list复制构造函数而不是移动构造函数来初始化向量。例如下面的代码打印copy

class A {
public:
    A(const string& ss): s(ss) {}
    A(A&& other) {
        cout << "move" << endl; 
        s = std::move(other.s); 
    }
    A(const A& other) {
        cout << "copy" << endl; 
        s = other.s; 
    }
    A& operator=(const A& other) {
        cout << "copy =" << endl;
        s = other.s;
        return *this; 
    }
    A& operator=(A&& other) {
        cout << "move =" << endl;
        s = std::move(other.s);
        return *this; 
    }
private:
    string s;
};

int main() {
    vector<A> as = vector{
        A("abc"),
    };
}
Run Code Online (Sandbox Code Playgroud)

这需要模板类型具有复制构造函数。有没有办法实现相同但仅使用移动构造函数?我知道push_back通过rvalue调用移动构造函数(例如下面的代码)。但是如果向量用多个元素初始化,它可能会很长。

int main() {
    vector<A> as;
    as.push_back(A("abc"));
}
// output "move"
Run Code Online (Sandbox Code Playgroud)

Rem*_*eau 5

std::initializer_list使副本所赋予它的价值。那些副本是const,所以如果它想移动它们vector甚至不能移动它们。因此,vector使用花括号初始化器列表初始化/分配 a将始终执行复制操作,而不是移动操作。

即使您将代码更改为使用A[]数组作为输入,将数组的迭代器传递给vector构造函数,vector仍然会从数组中复制值,而不是移动它们。

但是,您可以做的是使用std::vector::emplace()

int main() {
    A ghi("ghi");
    vector<A> as;
    as.emplace(as.end(), "abc", A("def"), std::move(ghi), ...);
}
Run Code Online (Sandbox Code Playgroud)

更新:或者,如果您有很多值要放入vector,并且您不想像这样做那样对列表进行硬编码,那么您可以使用该std::move()算法(不要与std::move()类型转换混淆),例如:

int main() {
    A items[] = {"abc", "def", "ghi", ...};
    // or whatever sequence you want to store the values in, 
    // as long as it supports iterators...

    vector<A> as;
    as.reserve(std::size(items));
    std::move(std::begin(items), std::end(items), std::back_inserter(as));
}
Run Code Online (Sandbox Code Playgroud)

这基本上只是一个std::vector::push_back()循环的包装器:

vector<A> as;
as.reserve(std::size(items));
for(auto &val : items) {
    as.push_back(std::move(val));
}
Run Code Online (Sandbox Code Playgroud)

更新:或者,您可以在构造函数中使用移动迭代器vector

vector<A> as(
    std::make_move_iterator(std::begin(items)),
    std::make_move_iterator(std::end(items))
);
Run Code Online (Sandbox Code Playgroud)