2011-03-30 247 views
22

如何將可變參數模板參數分成兩部分?例如:拆分可變參數模板參數

template <int d> struct a { 
    std::array <int, d> p, q; 
    template <typename ... T> a (T ... t) : p ({half of t...}), q ({other half of t...}) {} 
}; 
+0

有趣的問題。我想,因爲你可能可以將參數拆分爲元組,所以可以使用http://ideone.com/YyeNC作爲開始。令人遺憾的是,構造函數的多個參數包是非法的,即使在這種情況下,它應該清楚包中的類型是什麼。 – visitor 2011-03-30 11:57:09

回答

4

我們仍然缺乏操縱可變參數包(或者我不知道它們)的很多幫助。直到一個好的Boost庫將它們帶給我們,我們仍然可以編寫我們自己的。

例如,如果你願意你的陣列初始化推遲到構造體,您可以創建和使用fonction的參數包拷貝一部分到輸出迭代器:

#include <array> 
#include <cassert> 
#include <iostream> 

// Copy n values from the parameter pack to an output iterator 
template < typename OutputIterator > 
void copy_n(size_t n, OutputIterator) 
{ 
    assert (n == 0); 
} 

template < typename OutputIterator, typename T, typename... Args > 
void copy_n(size_t n, OutputIterator out, const T & value, Args... args) 
{ 
    if (n > 0) 
    { 
    *out = value; 
    copy_n(n - 1, ++out, args...); 
    } 
} 

// Copy n values from the parameter pack to an output iterator, starting at 
// the "beginth" element 
template < typename OutputIterator > 
void copy_range(size_t begin, size_t size, OutputIterator out) 
{ 
    assert(size == 0); 
} 


template < typename OutputIterator, typename T, typename... Args > 
void copy_range(size_t begin, size_t size, OutputIterator out, T value, Args... args) 
{ 
    if (begin == 0) 
    { 
    copy_n(size, out, value, args...); 
    } 
    else 
    { 
    copy_range(begin - 1, size, out, args...); 
    } 
} 


template < int N > 
struct DoubleArray 
{ 
    std::array< int, N > p; 
    std::array< int, N > q; 

    template < typename... Args > 
    DoubleArray (Args... args) 
    { 
    copy_range(0, N, p.begin(), args...); 
    copy_range(N, N, q.begin(), args...); 
    } 

}; 

int main() 
{ 
    DoubleArray<3> mya(1, 2, 3, 4, 5, 6); 
    std::cout << mya.p[0] << mya.p[2] << std::endl; 
    std::cout << mya.q[0] << mya.q[2] << std::endl; 
} 

// Ouput: 
// 13 
// 46 

,你可以看,你可以(不那麼容易)創建你自己的算法來操作參數包;所有需要的是對遞歸和模式匹配的一個很好的理解(就像在進行Template MetaProgramming時一樣)。

+0

這樣的增強庫已經存在:boost.fusion。如果將參數包轉換爲一個'tuple',boost.fusion可以用來將'tuple'拆分成兩個單獨的'tuple'或'boost :: fusion :: vector's。我編寫了一個演示代碼,但目前我只有VC++方便,缺乏可變參數模板。 – ildjarn 2011-03-30 18:44:51

+0

我知道Boost.Fusion庫,但是我正在考慮處理參數包的遞歸性質的庫:我們應該能夠操作參數包,而不首先將它轉換爲元組,這需要進行不必要的計算。不過,我確實可以通過使用Fusion來簡化代碼(也許我會發布另一個答案來說明如何完成這項工作)。 – 2011-03-31 07:29:17

10

Luc的解決方案簡潔明瞭,但缺乏樂趣。
因爲只有一個使用可變參數模板有道,這是濫用他們做瘋狂的過於複雜的元編程的東西:)

像這樣:

template <class T, size_t... Indx, class... Ts> 
std::array<T, sizeof...(Indx)> 
split_array_range_imp(pack_indices<Indx...> pi, Ts... ts) 
{ 
    return std::array<T, sizeof...(Indx)>{get<Indx>(ts...)...}; //TADA 
} 


template <class T, size_t begin, size_t end, class... Ts> 
std::array<T, end - begin> 
split_array_range(Ts... ts) 
{ 
    typename make_pack_indices<end, begin>::type indices; 
    return split_array_range_imp<T>(indices, ts...); 
} 

template <size_t N> 
struct DoubleArray 
{ 
    std::array <int, N> p, q; 

    template <typename ... Ts> 
    DoubleArray (Ts ... ts) : 
    p(split_array_range<int, 0    , sizeof...(Ts)/2 >(ts...)), 
    q(split_array_range<int, sizeof...(Ts)/2, sizeof...(Ts)  >(ts...)) 
    { 
    } 
}; 

int main() 
{ 
    DoubleArray<3> mya{1, 2, 3, 4, 5, 6}; 
    std::cout << mya.p[0] << "\n" << mya.p[1] << "\n" << mya.p[2] << std::endl; 
    std::cout << mya.q[0] << "\n" << mya.q[1] << "\n" << mya.q[2] << std::endl; 
} 

這是很短,但我們需要來編碼一些幫助程序:

首先,我們需要make_pack_indices結構,它用於在編譯時生成一個整數範圍。例如make_pack_indices<5, 0>::type實際上是類型pack_indices<0, 1, 2, 3, 4>

template <size_t...> 
struct pack_indices {}; 

template <size_t Sp, class IntPack, size_t Ep> 
struct make_indices_imp; 

template <size_t Sp, size_t ... Indices, size_t Ep> 
struct make_indices_imp<Sp, pack_indices<Indices...>, Ep> 
{ 
    typedef typename make_indices_imp<Sp+1, pack_indices<Indices..., Sp>, Ep>::type type; 
}; 

template <size_t Ep, size_t ... Indices> 
struct make_indices_imp<Ep, pack_indices<Indices...>, Ep> 
{ 
    typedef pack_indices<Indices...> type; 
}; 

template <size_t Ep, size_t Sp = 0> 
struct make_pack_indices 
{ 
    static_assert(Sp <= Ep, "__make_tuple_indices input error"); 
    typedef typename make_indices_imp<Sp, pack_indices<>, Ep>::type type; 
}; 

我們還需要一個get()函數,非常相似到std ::得到的元組,如std::get<N>(ts...)回報參數組的第N個元素。

template <class R, size_t Ip, size_t Ij, class... Tp> 
struct Get_impl 
{ 
    static R& dispatch(Tp...); 
}; 

template<class R, size_t Ip, size_t Jp, class Head, class... Tp> 
struct Get_impl<R, Ip, Jp, Head, Tp...> 
{ 
    static R& dispatch(Head& h, Tp&... tps) 
    { 
     return Get_impl<R, Ip, Jp + 1, Tp...>::dispatch(tps...); 
    } 
}; 

template<size_t Ip, class Head, class... Tp> 
struct Get_impl<Head, Ip, Ip, Head, Tp...> 
{ 
    static Head& dispatch(Head& h, Tp&... tps) 
    { 
     return h; 
    } 
}; 


template <size_t Ip, class ... Tp> 
typename pack_element<Ip, Tp...>::type& 
get(Tp&... tps) 
{ 
    return Get_impl<typename pack_element<Ip, Tp...>::type, Ip, 0, Tp...>::dispatch(tps...); 
} 

但建立的get(),我們還需要一個幫手pack_element結構,又非常相似到std :: tuple_element,如pack_element<N, Ts...>::type是第N類型的參數組。

template <size_t _Ip, class _Tp> 
class pack_element_imp; 

template <class ..._Tp> 
struct pack_types {}; 

template <size_t Ip> 
class pack_element_imp<Ip, pack_types<> > 
{ 
public: 
    static_assert(Ip == 0, "tuple_element index out of range"); 
    static_assert(Ip != 0, "tuple_element index out of range"); 
}; 

template <class Hp, class ...Tp> 
class pack_element_imp<0, pack_types<Hp, Tp...> > 
{ 
public: 
    typedef Hp type; 
}; 

template <size_t Ip, class Hp, class ...Tp> 
class pack_element_imp<Ip, pack_types<Hp, Tp...> > 
{ 
public: 
    typedef typename pack_element_imp<Ip-1, pack_types<Tp...> >::type type; 
}; 

template <size_t Ip, class ...Tp> 
class pack_element 
{ 
public: 
    typedef typename pack_element_imp<Ip, pack_types<Tp...> >::type type; 
}; 

然後我們走了。
其實我不明白爲什麼pack_element和get()都不在標準庫中。那些助手是爲std :: tuple存在的,爲什麼不用參數包?

注意:我對pack_element和make_pack_indices的實現是在libC++中找到的std :: tuple_element和__make_tuple_indices實現的直接轉換。

+0

我認爲這是最乾淨的解決方案。我看的越多,我越喜歡它。 – Thomas 2011-04-01 21:12:54

+0

海事組織這應該是被接受的答案,因爲這是正確的做法。在運行時複製元素具有相同的效果,但帶來的性能損失完全可以避免。 – 2012-07-16 20:28:15

+0

使用const引用而不是引用更好嗎?在哪些情況下會使用參考文獻?我編譯整個事情使用const引用,而不是它工作正常。 – prestokeys 2014-06-17 23:24:43

0

這裏又是另一種解決方案:

#include <array> 
#include <tuple> 
#include <iostream> 

template <int i, int o> struct cpyarr_ { 
    template < typename T, typename L > static void f (T const& t, L &l) { 
    l[i-1] = std::get<i-1+o> (t); 
    cpyarr_<i-1,o>::f (t,l); 
    } 
}; 

template <int o> struct cpyarr_ <0,o> { 
    template < typename T, typename L > static void f (T const&, L&) {} 
}; 

template <int i, int o, typename U, typename ... T> std::array < U, i > cpyarr (U u, T... t) { 
    std::tuple < U, T... > l { u, t... }; 
    std::array < U, i > a; 
    cpyarr_<i,o>::f (l, a); // because std::copy uses call to memmov which is not optimized away (at least with g++ 4.6) 
    return a; 
} 

template <int d> struct a { 
    std::array <int, d> p, q; 
    template <typename ... T> a (T ... t) : p (cpyarr<d,0> (t...)), q (cpyarr<d,d> (t...)) {} 
}; 

int main() { 
    a <5> x { 0,1,2,3,4,5,6,7,8,9 }; 
    for (int i = 0; i < 5; i++) 
    std::cout << x.p[i] << " " << x.q[i] << "\n"; 
} 
0

我知道這個問題是很老,但我發現它只是昨天在尋找一個解決一個非常類似的問題。我自己制定了一個解決方案,最後寫了一個小型的圖書館,我相信這是你想要的。如果你還有興趣,你可以找到描述here

2

注意,在這種特殊情況下,你可以使用std::initializer_list

template<int... Is> struct index_sequence{}; 

template<int N, int... Is> struct make_index_sequence 
{ 
    typedef typename make_index_sequence<N - 1, N - 1, Is...>::type type; 
}; 

template<int... Is> struct make_index_sequence<0, Is...> 
{ 
    typedef index_sequence<Is...> type; 
}; 

template <int d> struct a { 
    std::array <int, d> p, q; 

    constexpr a (const std::initializer_list<int>& t) : 
     a(t, typename make_index_sequence<d>::type()) 
    {} 

private: 
    template <int... Is> 
    constexpr a(const std::initializer_list<int>& t, index_sequence<Is...>) : 
     p ({{(*(t.begin() + Is))...}}), 
     q ({{(*(t.begin() + d + Is))...}}) 
    {} 
};