[英]variadic template function to concatenate std::vector containers
While learning about template parameter packs, I'm trying to write a clever, simple function to efficiently append two or more std::vector
containers together. 在学习模板参数包时,我正在尝试编写一个聪明,简单的函数来有效地将两个或多个
std::vector
容器附加在一起。
Below are two initial solutions. 以下是两个初始解决方案。
Version 1 is elegant but buggy, as it relies on side-effects during the expansion of the parameter pack, and the order of evaluation is undefined. 版本1优雅但有缺陷,因为它在参数包的扩展期间依赖于副作用,并且评估的顺序是未定义的。
Version 2 works, but relies on a helper function that requires two cases. 版本2有效,但依赖于需要两种情况的辅助函数。 Yuck.
呸。
Can you see if you can come up with a simpler solution? 你能看出你是否能想出一个更简单的解决方案吗? (For efficiency, the vector data should not be copied more than once.)
(为了提高效率,不应多次复制矢量数据。)
#include <vector>
#include <iostream>
// Append all elements of v2 to the end of v1.
template<typename T>
void append_to_vector(std::vector<T>& v1, const std::vector<T>& v2) {
for (auto& e : v2) v1.push_back(e);
}
// Expand a template parameter pack for side effects.
template<typename... A> void ignore_all(const A&...) { }
// Version 1: Concatenate two or more std::vector<> containers into one.
// Nicely simple, but buggy as the order of evaluation is undefined.
template<typename T, typename... A>
std::vector<T> concat1(std::vector<T> v1, const A&... vr) {
// Function append_to_vector() returns void, so I enclose it in (..., 1).
ignore_all((append_to_vector(v1, vr), 1)...);
// In fact, the evaluation order is right-to-left in gcc and MSVC.
return v1;
}
// Version 2:
// It works but looks ugly.
template<typename T, typename... A>
void concat2_aux(std::vector<T>& v1, const std::vector<T>& v2) {
append_to_vector(v1, v2);
}
template<typename T, typename... A>
void concat2_aux(std::vector<T>& v1, const std::vector<T>& v2, const A&... vr) {
append_to_vector(v1, v2);
concat2_aux(v1, vr...);
}
template<typename T, typename... A>
std::vector<T> concat2(std::vector<T> v1, const A&... vr) {
concat2_aux(v1, vr...);
return v1;
}
int main() {
const std::vector<int> v1 { 1, 2, 3 };
const std::vector<int> v2 { 4 };
const std::vector<int> v3 { 5, 6 };
for (int i : concat1(v1, v2, v3)) std::cerr << " " << i;
std::cerr << "\n"; // gcc output is: 1 2 3 5 6 4
for (int i : concat2(v1, v2, v3)) std::cerr << " " << i;
std::cerr << "\n"; // gcc output is: 1 2 3 4 5 6
}
A helper type: I dislike using int
for it. 帮手类型:我不喜欢使用
int
。
struct do_in_order { template<class T>do_in_order(T&&){}};
Add up sizes:' 添加尺寸:'
template<class V>
std::size_t sum_size( std::size_t& s, V&& v ) {return s+= v.size(); }
Concat. 的毗连。 Returns type to be ignored:
返回要忽略的类型:
template<class V>
do_in_order concat_helper( V& lhs, V const& rhs ) { lhs.insert( lhs.end(), rhs.begin(), rhs.end() ); return {}; }
Micro optimization, and lets you concat vectors of move only types: 微优化,并允许您连接仅移动类型的向量:
template<class V>
do_in_order concat_helper( V& lhs, V && rhs ) { lhs.insert( lhs.end(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()) ); return{}; }
actual function. 实际功能。 Above stuff should be in a details namespace:
以上内容应该在详细信息命名空间中:
template< typename T, typename A, typename... Vs >
std::vector<T,A> concat( std::vector<T,A> lhs, Vs&&...vs ){
std::size s=lhs.size();
do_in_order _0[]={ sum_size(s,vs)..., 0 };
lhs.reserve(s);
do_in_order _1[]={ concat_helper( lhs, std::forward<Vs>(vs) )..., 0 };
return std::move(lhs); // rvo blocked
}
apologies for any typos. 为任何错别字道歉。
There is a related answer on concatenation of strings: https://stackoverflow.com/a/21806609/1190077 . 有关字符串连接的相关答案: https : //stackoverflow.com/a/21806609/1190077 。 Adapted here, it looks like:
在这里改编,它看起来像:
template<typename T, typename... A>
std::vector<T> concat_version3(std::vector<T> v1, const A&... vr) {
int unpack[] { (append_to_vector(v1, vr), 0)... };
(void(unpack));
return v1;
}
This seems to work! 这似乎工作!
However, is the evaluation order of the template parameter pack now well-defined, or is it by accident that the compiler did the right thing? 但是,模板参数包的评估顺序现在定义得很好,还是编译器做了正确的事情是偶然的?
The answer by Yakk ( https://stackoverflow.com/a/23439527/1190077 ) works well. Yakk( https://stackoverflow.com/a/23439527/1190077 )的答案很有效。
Here is a polished version, incorporating my improvement to do_in_order
and removing the sum_size
external function: 这是一个精致的版本,将我的改进结合到
do_in_order
并删除sum_size
外部函数:
// Nice syntax to allow in-order expansion of parameter packs.
struct do_in_order {
template<typename T> do_in_order(std::initializer_list<T>&&) { }
};
namespace details {
template<typename V> void concat_helper(V& l, const V& r) {
l.insert(l.end(), r.begin(), r.end());
}
template<class V> void concat_helper(V& l, V&& r) {
l.insert(l.end(), std::make_move_iterator(r.begin()),
std::make_move_iterator(r.end()));
}
} // namespace details
template<typename T, typename... A>
std::vector<T> concat(std::vector<T> v1, A&&... vr) {
std::size_t s = v1.size();
do_in_order { s += vr.size() ... };
v1.reserve(s);
do_in_order { (details::concat_helper(v1, std::forward<A>(vr)), 0)... };
return std::move(v1); // rvo blocked
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.