我可以从基于范围的 for 中移动元素吗?
Can I move elements from a range-based for?
假设我有这样的代码:
std::vector<std::string> produce(const std::string& str){
// create a vector based on input
}
void consume(const std::string& str){
for (auto i:produce(str))
// do some thing that use the str
// and I'd like to move it from the vector
some_process(str) // for example, move into this function
}
我只是想知道编译器(我可能使用 VS2015 或 gcc 6)是否可以优化以将元素移动到 for 循环中。或者我应该怎么做才能让它移动,因为字符串可能很长。
旧的 begin-to-end for 循环或协程会有帮助吗?
如果您想将该向量中的元素移动到 some_function()
,只需明确地执行 move
:
void some_function( std::string str );
void some_function( std::string &&str ); // or explicitly
for(auto &i:produce(str))
some_function( std::move(i) );
否则不清楚你所说的将元素移动到for循环中是什么意思
只需 auto&
和明确的 std::move
就足够了。
不过要花哨
struct empty_t{};
template<class It,class B=empty_t>struct range_t:B{
It b,e;
It begin()const{return b;}
It end()const{return e;}
range_t(It s,It f):b(std::move(s)),e(std::move(f)){}
// fancy
template<class S, class F>
range_t(B base, S s, F f):B(std::move(base)),b(s(*this)),e(f(*this)){}
};
template<class It>range_t<It> range(It b, It e){return{std::move(b),std::move(e)};}
template<class B, class S, class F>
auto range( B base, S s, F f ){
auto It=std::result_of_t<s(base)>;
return range_t<It,B>{
std::move(base),std::move(s),std::move(f)
};
}
template<class R>
auto move_from(R& r){
using std::begin; using std::end;
return range( std::make_move_iterator(begin(r)), std::make_move_iterator(end(r)) );
}
template<class R>
auto move_from(R&& r){
using std::begin; using std::end;
return range(
std::move(r),
[](auto&r){return std::make_move_iterator(begin(r));},
[](auto&r){return std::make_move_iterator(end(r));}
);
}
现在,除了错别字,
for(auto i:move_from(produce(str)))
some_function( std::move(i) );
将 i
作为每个元素的移动副本。
但这太疯狂了。
当您迭代基于 ranfe/container 的代码并且希望移动不可知时,此技术可能很有用。
template<class R, class F>
auto transform_to_vector( R&& r, F&& f ){
using std::begin; using std::end;
using rT=std::decay_t<std::result_of_t< f(*begin(std::forward<R>(r))) >>;
std::vector<rT> retval;
for(auto&& e:std::forward<R>(r)){
retval.push_back( f(decltype(e)(e)) );
}
return retval;
}
现在,用 move_from(x)
调用上面的代码,因为你的 "range" 不同于用 x
调用它。你可以想象其他算法也是这样写的。
假设我有这样的代码:
std::vector<std::string> produce(const std::string& str){
// create a vector based on input
}
void consume(const std::string& str){
for (auto i:produce(str))
// do some thing that use the str
// and I'd like to move it from the vector
some_process(str) // for example, move into this function
}
我只是想知道编译器(我可能使用 VS2015 或 gcc 6)是否可以优化以将元素移动到 for 循环中。或者我应该怎么做才能让它移动,因为字符串可能很长。
旧的 begin-to-end for 循环或协程会有帮助吗?
如果您想将该向量中的元素移动到 some_function()
,只需明确地执行 move
:
void some_function( std::string str );
void some_function( std::string &&str ); // or explicitly
for(auto &i:produce(str))
some_function( std::move(i) );
否则不清楚你所说的将元素移动到for循环中是什么意思
只需 auto&
和明确的 std::move
就足够了。
不过要花哨
struct empty_t{};
template<class It,class B=empty_t>struct range_t:B{
It b,e;
It begin()const{return b;}
It end()const{return e;}
range_t(It s,It f):b(std::move(s)),e(std::move(f)){}
// fancy
template<class S, class F>
range_t(B base, S s, F f):B(std::move(base)),b(s(*this)),e(f(*this)){}
};
template<class It>range_t<It> range(It b, It e){return{std::move(b),std::move(e)};}
template<class B, class S, class F>
auto range( B base, S s, F f ){
auto It=std::result_of_t<s(base)>;
return range_t<It,B>{
std::move(base),std::move(s),std::move(f)
};
}
template<class R>
auto move_from(R& r){
using std::begin; using std::end;
return range( std::make_move_iterator(begin(r)), std::make_move_iterator(end(r)) );
}
template<class R>
auto move_from(R&& r){
using std::begin; using std::end;
return range(
std::move(r),
[](auto&r){return std::make_move_iterator(begin(r));},
[](auto&r){return std::make_move_iterator(end(r));}
);
}
现在,除了错别字,
for(auto i:move_from(produce(str)))
some_function( std::move(i) );
将 i
作为每个元素的移动副本。
但这太疯狂了。
当您迭代基于 ranfe/container 的代码并且希望移动不可知时,此技术可能很有用。
template<class R, class F>
auto transform_to_vector( R&& r, F&& f ){
using std::begin; using std::end;
using rT=std::decay_t<std::result_of_t< f(*begin(std::forward<R>(r))) >>;
std::vector<rT> retval;
for(auto&& e:std::forward<R>(r)){
retval.push_back( f(decltype(e)(e)) );
}
return retval;
}
现在,用 move_from(x)
调用上面的代码,因为你的 "range" 不同于用 x
调用它。你可以想象其他算法也是这样写的。