[英]C++ zip variadic templates
這是C ++中一個簡單的雙容器zip函數:
template <typename A, typename B>
std::list<std::pair<A, B> > simple_zip(const std::list<A> & lhs,
const std::list<B> & rhs)
{
std::list<std::pair<A, B> > result;
for (std::pair<typename std::list<A>::const_iterator,
typename std::list<B>::const_iterator> iter
=
std::pair<typename std::list<A>::const_iterator,
typename std::list<B>::const_iterator>(lhs.cbegin(),
rhs.cbegin());
iter.first != lhs.end() && iter.second != rhs.end();
++iter.first, ++iter.second)
{
result.push_back( std::pair<A, B>(*iter.first, *iter.second) );
}
return result;
}
如何將此擴展到具有可變參數模板的任意數量的容器?
我希望general_zip
接受list
的tuple
(每個列表可以包含不同的類型)並返回tuple
list
。
看起來這應該有效
std::list<std::tuple<>> simple_zip() {
return {};
}
template <typename ...T>
std::list<std::tuple<T...>> simple_zip(std::list<T>... lst)
{
std::list<std::tuple<T...>> result;
for (int i = 0, e = std::min({lst.size()...}); i != e; i++) {
result.emplace_back(std::move(lst.front())...);
[](...){} ((lst.pop_front(), 0)...);
}
return result;
}
@Potatoswatter有一個好的(IMO)評論說,當列表大小不同時,這可能會復制超過需要的東西,並且只使用迭代器會更好,因為pop_front比實際需要更多。 我認為以下“修復”迭代器以更多代碼為代價。
template <typename ...T>
std::list<std::tuple<T...>> simple_zip(std::list<T>... lst)
{
std::list<std::tuple<T...>> result;
struct {
void operator()(std::list<std::tuple<T...>> &t, int c,
typename std::list<T>::iterator ...it) {
if(c == 0) return;
t.emplace_back(std::move(*it++)...);
(*this)(t, c-1, it...);
}
} zip;
zip(result, std::min({lst.size()...}), lst.begin()...);
return result;
}
std::list<std::tuple<>> simple_zip() {
return {};
}
這是約翰內斯第一個答案的漸進式改進。 它避免了虛擬struct
並避免復制整個輸入列表(盡管除非一個列表比其他列表短,否則這並不重要)。 我也把它作為所有容器的通用。
但它需要一個樣板包裝索引生成器,無論如何都非常有用
template< std::size_t n, typename ... acc >
struct make_index_tuple {
typedef typename make_index_tuple<
n - 1,
std::integral_constant< std::size_t, n - 1 >, acc ...
>::type type;
};
template< typename ... acc >
struct make_index_tuple< 0, acc ... >
{ typedef std::tuple< acc ... > type; };
“真正的”實現包括一個簡單的函數,它需要上面的實用程序的輸出,以及一個將包映射到元組的接口函數。
template< typename ret_t, std::size_t ... indexes, typename lst_tuple >
ret_t simple_zip( std::tuple< std::integral_constant< std::size_t, indexes > ... >,
lst_tuple const &lst ) {
ret_t ret;
auto iters = std::make_tuple( std::get< indexes >( lst ).begin() ... );
auto ends = std::make_tuple( std::get< indexes >( lst ).end() ... );
while ( std::max< bool >({ std::get< indexes >( iters )
== std::get< indexes >( ends ) ... }) == false ) {
ret.emplace_back( * std::get< indexes >( iters ) ++ ... );
}
return ret;
}
template< typename ... T >
std::list< std::tuple< typename T::value_type ... > >
simple_zip( T const & ... lst ) {
return simple_zip
< std::list< std::tuple< typename T::value_type ... > > > (
typename make_index_tuple< sizeof ... lst >::type(),
std::tie( lst ... )
);
}
至少,這使得約翰內斯看起來很容易。 這是大多數可變參數模板算法的樣子,因為沒有tuple
就無法存儲類型 - 可變參數狀態,並且沒有方法可以在沒有索引包或元遞歸函數的情況下處理可變參數元組。 (編輯:嗯,現在Johannes使用尾遞歸本地函數來定義本地包來完成所有沒有tuple
的東西。真棒......如果你能處理所有的函數式編程; v)。)
另一個版本:Johannes和Potatoswatter的混合答案,試圖盡量減少欺騙的數量(盡管如此我還是喜歡!)
template <typename C, typename... Its>
void simple_zip_details(C& c, size_t size, Its... its)
{
for (int i = 0; i < size; i++)
c.emplace_back(std::move(*its++)...);
}
template <typename... Ts>
std::list<std::tuple<Ts...>> simple_zip(std::list<Ts>... lst)
{
std::list<std::tuple<Ts...>> result;
size_t size = std::min({ lst.size()... });
simple_zip_details(result, size, lst.begin()...);
return result;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.