[英]Expand two parameter packs
請考慮以下代碼:
static constexpr size_t Num {2};
struct S {
std::array<size_t, Num> get () { return {1, 2}; }
};
struct S1 : S {};
struct S2 : S {};
struct M {
template <typename T>
typename std::enable_if<std::is_same<T, S1>::value, S1>::type get () const {
return S1 {};
}
template <typename T>
typename std::enable_if<std::is_same<T, S2>::value, S2>::type get () const {
return S2 {};
}
};
我想要一個函數,它合並兩個或多個std::array
s,構成一個std::array
。
到目前為止,我結束了這樣的事情:
template <typename Mode, typename... Rs, size_t... Ns>
std::array<size_t, sizeof... (Rs)*Num> get_array (const Mode& mode, Sequence::Sequence<Ns...>) {
return {std::get<Ns> (mode.template get<Rs...> ().get ())...};
}
我希望得到以下代碼
M m;
auto x = get_array<M, S1, S2> (m, Sequence::Make<2> {});
生成std::array<size_t, 4>
填充{1, 2, 1, 2}
std::array<size_t, 4>
{1, 2, 1, 2}
。
其中Sequence::Sequence
和Sequence::Make
在這里描述 。
我知道放置...
的Rs
是不正確在這種情況下(如果sizeof... (Rs)
是1,那么它是好的, std::array<size_t, 2>
與{1, 2}
被返回),但我不知道在哪里進行擴展看起來像這樣:
std::get<0> (mode.template get<Rs[0]> ().get ()),
std::get<1> (mode.template get<Rs[0]> ().get ()),
std::get<0> (mode.template get<Rs[1]> ().get ()),
std::get<1> (mode.template get<Rs[1]> ().get ());
當然Rs[0]
我的意思是參數包中的第一種類型。
它甚至可能嗎?
假設我們正在使用Xeo的索引序列實現,我們可以這樣做:
首先創建一個用於連接兩個數組的函數。 它接收數組,加上每個數組的索引序列( detail::seq
是index_sequence
類型)
template<class T, size_t N, size_t M, size_t... I, size_t... J>
std::array<T, N + M> concat(const std::array<T, N>& arr1, const std::array<T, M>& arr2, detail::seq<I...>, detail::seq<J...>)
{
return {arr1[I]..., arr2[J]...};
}
接下來,從你的get_array
函數調用這個函數,除了我們要將從main
調用中收到的seq
加倍:
template<class MODE, class... T, size_t... I>
auto get_array(MODE m, detail::seq<I...>) ->decltype(concat(m.template get<T>().get()..., detail::seq<I...>{}, detail::seq<I...>{})){
return concat(m.template get<T>().get()..., detail::seq<I...>{}, detail::seq<I...>{});
}
main
的調用看起來就像在代碼中一樣:
M m;
auto x = get_array<M, S1, S2>(m, detail::gen_seq<2>{});
detail::gen_seq
是Xeo所具有的make_index_sequence
的實現。
請注意,我在Xeo的索引序列impl中將unsigned
替換為size_t
。
在C ++ 14中,我們不需要實現seq
或gen_seq
,在函數之后我們也不需要尾隨-> decltype()
。
在C ++ 17中,使用折疊表達式來概括任意數量的數組的連接將更容易。
是的,這可以用標准的index_sequence
技巧完成:
template <class T, std::size_t N1, std::size_t N2, std::size_t ... Is, std::size_t ... Js>
std::array<T, N1 + N2> merge_impl(const std::array<T, N1>& a1,
const std::array<T, N2>& a2,
std::index_sequence<Is...>,
std::index_sequence<Js...>) {
return {a1[Is]..., a2[Js]...};
}
template <class T, std::size_t N1, std::size_t N2>
std::array<T, N1 + N2> merge(const std::array<T, N1>& a1, const std::array<T, N2>& a2) {
return merge_impl(a1, a2,
std::make_index_sequence<N1>{},
std::make_index_sequence<N2>{});
}
index_sequence
僅在14個標准中,但可以很容易地在11中實現; 有許多資源(包括在SO上)描述了如何這樣做(編輯:它基本上等同於你的Sequence
東西,也可以習慣它們的標准名稱)。 實例: http : //coliru.stacked-crooked.com/a/54dce4a695357359 。
首先,這基本上要求連接任意數量的數組。 這與連接任意數量的元組非常相似,即使在C ++ 11中也存在標准庫函數: std::tuple_cat()
。 這讓我們幾乎到了那里:
template <class... Ts, class M>
auto get_array(M m) -> decltype(std::tuple_cat(m.template get<Ts>()...)) {
return std::tuple_cat(m.template get<Ts>()...);
}
請注意,我翻轉了模板參數,因此這只是get_array<T1, T2>(m)
而不是必須寫入get_array<M, T1, T2>(m)
。
現在的問題是,我們如何編寫array_cat
? 我們只使用tuple_cat
並將生成的tuple
轉換為array
。 假設index_sequence
的實現是可用的(無論如何,這是您在集合中想要的):
template <class T, class... Ts, size_t... Is>
std::array<T, sizeof...(Ts)+1> to_array_impl(std::tuple<T, Ts...>&& tup,
std::index_sequence<Is...> ) {
return {{std::get<Is>(std::move(tup))...}};
}
template <class T, class... Ts>
std::array<T, sizeof...(Ts)+1> to_array(std::tuple<T, Ts...>&& tup) {
return to_array_impl(std::move(tup), std::index_sequence_for<T, Ts...>());
}
template <class... Tuples>
auto array_cat(Tuples&&... tuples) -> decltype(to_array(std::tuple_cat(std::forward<Tuples>(tuples)...))) {
return to_array(std::tuple_cat(std::forward<Tuples>(tuples)...));
}
這會給你:
template <class... Ts, class M>
auto get_array(M m) -> decltype(array_cat(m.template get<Ts>()...)) {
return array_cat(m.template get<Ts>()...);
}
它處理任意多種類型。
所以這里是任意數量的相同類型的數組。 我們基本上實現了一個高度限制性的tuple_cat
版本,因為數組中元素的數量是相同的,因此變得非常容易。 我使用了幾個C ++ 14和17庫的特性,這些特性在C ++ 11中都很容易實現。
template<class, size_t> struct div_sequence;
template<size_t...Is, size_t Divisor>
struct div_sequence<std::index_sequence<Is...>, Divisor>
{
using quot = std::index_sequence<Is / Divisor...>;
using rem = std::index_sequence<Is % Divisor...>;
};
template<class T, size_t...Ns, size_t...Is, class ToA>
std::array<T, sizeof...(Ns)> array_cat_impl(std::index_sequence<Ns...>,
std::index_sequence<Is...>,
ToA&& t)
{
// NB: get gives you perfect forwarding; [] doesn't.
return {std::get<Is>(std::get<Ns>(std::forward<ToA>(t)))... };
}
template<class Array, class... Arrays,
class VT = typename std::decay_t<Array>::value_type,
size_t S = std::tuple_size<std::decay_t<Array>>::value,
size_t N = S * (1 + sizeof...(Arrays))>
std::array<VT, N> array_cat(Array&& a1, Arrays&&... as)
{
static_assert(std::conjunction_v<std::is_same<std::decay_t<Array>,
std::decay_t<Arrays>>...
>, "Array type mismatch");
using ind_seq = typename div_sequence<std::make_index_sequence<N>, S>::rem;
using arr_seq = typename div_sequence<std::make_index_sequence<N>, S>::quot;
return array_cat_impl<VT>(arr_seq(), ind_seq(),
std::forward_as_tuple(std::forward<Array>(a1),
std::forward<Arrays>(as)...)
);
}
我們也可以重用tuple_cat
機制,就像在@ Barry的回答中一樣。 為避免潛在的QoI問題,避免依賴擴展和額外的移動,我們不希望直接使用tuple_cat
std::array
。 相反,我們首先將數組轉換為引用元組。
template<class TupleLike, size_t... Is>
auto as_tuple_ref(TupleLike&& t, std::index_sequence<Is...>)
-> decltype(std::forward_as_tuple(std::get<Is>(std::forward<TupleLike>(t))...))
{
return std::forward_as_tuple(std::get<Is>(std::forward<TupleLike>(t))...);
}
template<class TupleLike,
size_t S = std::tuple_size<std::decay_t<TupleLike>>::value >
auto as_tuple_ref(TupleLike&& t)
-> decltype(as_tuple_ref(std::forward<TupleLike>(t), std::make_index_sequence<S>()))
{
return as_tuple_ref(std::forward<TupleLike>(t), std::make_index_sequence<S>());
}
然后我們可以將tuple_cat
的d引用轉換回數組:
template <class R1, class...Rs, size_t... Is>
std::array<std::decay_t<R1>, sizeof...(Is)>
to_array(std::tuple<R1, Rs...> t, std::index_sequence<Is...>)
{
return { std::get<Is>(std::move(t))... };
}
template <class R1, class...Rs>
std::array<std::decay_t<R1>, sizeof...(Rs) + 1> to_array(std::tuple<R1, Rs...> t)
{
static_assert(std::conjunction_v<std::is_same<std::decay_t<R1>, std::decay_t<Rs>>...>,
"Array element type mismatch");
return to_array(t, std::make_index_sequence<sizeof...(Rs) + 1>());
}
最后, array_cat
本身就是
template <class... Arrays>
auto array_cat(Arrays&&... arrays)
-> decltype(to_array(std::tuple_cat(as_tuple_ref(std::forward<Arrays>(arrays))...)))
{
return to_array(std::tuple_cat(as_tuple_ref(std::forward<Arrays>(arrays))...));
}
任何體面的優化器都應該很難優化參考的中間元組。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.