Min*_*ine 7 c++ move initializer-list c++11
考虑以下代码:
#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();
}
Run Code Online (Sandbox Code Playgroud)
编译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.
Run Code Online (Sandbox Code Playgroud)
请注意,这两个函数都使用复制省略,因此std::vector<C>不会复制返回的内容.
我明白为什么h()用move-constructor,但为什么g()用copy-constructor?
来自vector的文档:
(6)初始化列表构造函数
构造一个容器,其中包含il中每个元素的副本,顺序相同.
看起来初始化列表总是复制元素,然后可能意味着initializer-list constructor如果C移动便宜但复制很重,那么性能可能会受到影响.
所以我的问题是:初始化容器(例如vector)的首选方法是什么,移动成本低但复制的对象?
您可以使用一些样板文件从初始值设定项列表中移动。
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()};
}
};
Run Code Online (Sandbox Code Playgroud)
使用:
std::vector<C> v=make_container<C>{ {}, {} };
Run Code Online (Sandbox Code Playgroud)
这简洁、高效,可以解决您的问题。
(可能应该在operator T&&上面。不确定,而且我对返回右值引用持怀疑态度......)
现在,这看起来有点像黑客。但是,其他选择很糟糕。
手动推回/安置回列表很丑陋,并且在添加最大效率的储备要求后变得更丑陋。而天真的il解决方案无法移动。
在我看来,不允许您在声明实例的地方列出元素的解决方案很尴尬。您希望能够将内容列表放在声明旁边。
另一个“本地列表”替代方案是创建一个可变函数,该std::array函数在内部初始化一个(可能是引用包装器),然后从该数组移动到容器中。然而,这不允许{ {}, {}, {} }样式列表,所以我发现它缺乏。
我们可以这样做:
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))};
}
Run Code Online (Sandbox Code Playgroud)
然后:
C arr[]={{}, {}, {}};
std::vector<C> v = move_from_array(arr);
Run Code Online (Sandbox Code Playgroud)
唯一的缺点是在使用时需要两个声明。但代码没有我的第一个解决方案那么迟钝。
| 归档时间: |
|
| 查看次数: |
283 次 |
| 最近记录: |