gpt4 book ai didi

c++ - 使用 move 成本低但复制成本高的对象初始化容器的首选方法是什么

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

考虑下面的代码:

#include <iostream>
#include <vector>

struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
C(C&&) {std::cout << "A move was made.\n";}
};

std::vector<C> g() {
std::vector<C> ret {C(), C(), C()};
return ret;
}

std::vector<C> h() {
std::vector<C> ret;
ret.reserve(3);
ret.push_back(C());
ret.push_back(C());
ret.push_back(C());
return ret;
}

int main() {
std::cout << "Test g\n";
std::vector<C> v1 = g();

std::cout << "Test h\n";
std::vector<C> v2 = h();
}

编译自g++ -std=c++11 main.cpp && ./a.out ,结果为:

Test g
A copy was made.
A copy was made.
A copy was made.
Test h
A move was made.
A move was made.
A move was made.

请注意,这两个函数都使用复制省略,因此返回的 std::vector<C>不被复制。

我明白为什么h()使用 move-constructor , 但为什么 g()使用 copy-constructor

来自 vector's doc :

(6) initializer list constructor

Constructs a container with a copy of each of the elements in il, in the same order.

看起来 initializer-list 总是复制元素,那么它可能意味着 initializer-list constructor如果C,性能可能会受到影响 move 便宜但复制起来很重。

所以我的问题是:使用 move 成本低但复制成本高的对象来初始化容器(例如 vector)的首选方法是什么?

最佳答案

您可以使用一些样板从初始化列表中 move 。

template<class T>
struct force_move{
mutable T t;

template<class...Args>
force_move(Args&&...args):
t(std::forward<Args>(args)...)
{}
// todo: code that smartly uses {} if () does not work?

force_move()=default;
force_move(force_move const&)=delete;

template<class U, class...Args>
force_move(std::initializer_list<U> il, Args&&...args):
t(il, std::forward<Args>(args)...)
{}

operator T()const{ return std::move(t); }
};

template<class T>
struct make_container {
std::initializer_list<force_move<T>> il;
make_container( std::initializer_list<force_move<T>> l ):il(l) {}

template<class C>
operator C()&&{
return {il.begin(), il.end()};
}
};

使用:

std::vector<C> v=make_container<C>{ {}, {} };

简洁高效,解决了你的问题。

(可能应该是上面的 operator T&&。不确定,而且我对返回右值引用持怀疑态度...)

现在,这似乎有点不对劲。但是,替代方案很糟糕。

手动推回/安置回退列表很丑陋,并且在您添加保留要求以实现最大效率后变得更丑陋。天真的 il 解决方案无法 move 。

在我看来,不让您在那里列出声明实例的元素的解决方案很尴尬。您希望能够将内容列表放在声明旁边。

另一种“本地列表”替代方法是创建一个可变函数,它在内部初始化一个 std::array(可能是 ref 包装器),然后从该数组 move 到容器中。但是,这不允许 { {}, {}, {} } 样式列表,所以我发现它缺少。

我们可以这样做:

template<class T, std::size_t N>
std::vector<T> move_from_array( T(&arr)[N] ){
return {std::make_move_iterator(std::begin(arr)), std::make_move_iterator(std::end(arr))};
}

然后:

C arr[]={{}, {}, {}};
std::vector<C> v = move_from_array(arr);

唯一的缺点是在使用时需要两个声明。但是代码没有我的第一个解决方案那么晦涩。

关于c++ - 使用 move 成本低但复制成本高的对象初始化容器的首选方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38452645/

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