2015-10-16 21 views
3

正如標題所說我有一個可變參數模板接受至少3個參數(INT的)C++ 11可變參數模板:3..n INT的分成第一,中間,最後

template<int p_first, int p_second, int p_third, int... p_rest> 

和我需要這些分成第一中間最後

class MyClass { 
    OtherClass<p_first> first; 
    // Works obviously 

    // std::vector<OtherClass> middle... Doesn't work 

    OtherClass<p_last> last; 
    // No idea how to do this 
} 

的Visual Studio 2015年C++功能都可以


編輯:對不起,我忘了提及的關鍵方面。

謝謝你的職位。我會理解,儘快測試代碼並發表評論!

+0

因此,您需要根據您獲得的整數參數來確定最後一個值和中間值? –

+1

你對中間值做了什麼? – Barry

+1

您是否正在重塑[元組](http://en.cppreference.com/w/cpp/utility/tuple)? – Drop

回答

3

使用Boost.Hana(需要一個C++編譯器14):

#include <boost/hana.hpp> 
#include <tuple> 
namespace hana = boost::hana; 


template <int> 
struct OtherClass { }; 

template <int ...I> 
class MyClass { 
    static constexpr auto ints = hana::tuple_c<int, I...>; 

    OtherClass<hana::front(ints)> first; 

    using Middle = typename decltype(
     hana::unpack(hana::slice_c<1, sizeof...(I) - 1>(ints), [](auto ...i) { 
      return hana::type_c<std::tuple<OtherClass<ints[i]>...>>; 
     }) 
    )::type; 
    Middle middle; 

    OtherClass<hana::back(ints)> last; 
}; 

int main() { 
    MyClass<0, 3, 2, 1> x; 
} 

請注意,我在上面濫用一個鏘錯誤,因爲lambda表達式不允許出現在未評估的情況下。爲了符合標準,在調用hana::unpack時,應使用手寫函數對象而不是lambda表達式。另外,如果你使用Hana方式,如果編譯時性能是一個考慮因素,我會建議你使用hana::tuple,因爲std::tuple是所有已知標準庫實現中的一個緩存。

+0

Boost.Hana看起來非常有趣! (很好的網站和順便說一句)我得到它與您的代碼一起工作,但'OtherClass ...>'需要更改爲'OtherClass ...>'。我想你在那裏誤解了我的問題。無論如何,有了這個偉大的圖書館,它似乎完美無缺地工作! –

+0

謝謝!是的,你說'OtherClass 'vs'OtherClass ',我很困惑。但是,請注意,此解決方案暫時只能用於Clang,因爲GCC仍然存在與C++ 14相關的bug和MSVC,ICC&al仍然遠遠落後於C++ 14支持。所以如果MSVC支持是必須的,你應該使用別的東西。 –

7

你可以寫一個輔助結構從一個int包拿到Nint

template <std::size_t N, int... I> 
struct get_n : 
    std::integral_constant<int, 
     std::get<N>(std::array<int,sizeof...(I)> { I... }) 
    > 
{}; 

然後,你可以寫元函數來獲取中間和結尾:

template <int... I> 
using get_middle = get_n<sizeof...(I)/2 - 1, I...>; 

template <int... I> 
using get_end = get_n<sizeof...(I) - 1, I...>; 

您可以使用這樣如此:

using p_last = get_end<p_third, p_rest...>; 
OtherClass<p_last> last; 

如果你想爲所有中間元素設置一個OtherClass<N>的元組,這裏有一個相當簡單的解決方案。請參閱Yakk's answer以獲得更復雜,更靈活的一個。

template <template <int> class ToBuild, class Seq, int... Args> 
struct build_tuple; 

template <template <int> class ToBuild, std::size_t... Idx, int... Args> 
struct build_tuple<ToBuild, std::index_sequence<Idx...>, Args...> { 
    using type = std::tuple<ToBuild<get_n<Idx, Args...>::value>...>; 
}; 

template<int p_first, int p_second, int p_third, int... p_rest> 
struct MyClass { 
    MyClass() { 
     typename build_tuple<OtherClass, 
          std::make_index_sequence<sizeof...(p_rest) + 1>, 
          p_second, p_third, p_rest...>::type middle; 
    } 
}; 
7

首先,樣板。我在類型中工作,而不是常量,因爲使用類型進行元編程要容易得多。

template<class T>struct tag{using type=T;}; 
template<class Tag>using type_t=typename Tag::type; 

template<int I>using int_k=std::integral_constant<int, I>; 

template<class...>struct types{using type=types;}; 

template<template<class...>class Z, class pack> 
struct apply; 
template<template<class...>class Z, class pack> 
using apply_t=type_t<apply<Z,pack>>; 
template<template<class...>class Z, class...Ts> 
struct apply<Z, types<Ts...>>:tag<Z<Ts...>>{}; 

現在,一旦我們有中間元素的包裝,我們可以應用它們。

template <std::size_t N, class... Ts> 
using get_t = type_t< std::tuple_element< N, std::tuple<Ts...> > >; 

從類型列表中獲取第n個類型。

template <class Is, class pack> 
struct get_slice; 
template <class Is, class pack> 
using get_slice_t=type_t<get_slice<Is,pack>>; 
template<std::size_t...Is, class...Ts> 
struct get_slice<std::index_sequence<Is...>,types<Ts...>>: 
    types< get_t<Is, Ts...>... >{}; 

這讓我們拿一包,並從中得到一個切片。

偏移索引列:

template<class Is, std::size_t I> 
struct offset; 
template<class Is, std::size_t I> 
using offset_t=type_t<offset<Is,I>>; 
template<std::size_t...Is, size_t I> 
struct offset<std::index_sequence<Is...>, I>: 
    tag<std::index_sequence<(I+Is)...>> 
{}; 

提取中間分子開始在長度LEN的開始:

template<std::size_t start, std::size_t len, class pack> 
struct get_mid: 
    get_slice< offset_t< std::make_index_sequence<len>, start >, pack > 
{}; 
template<std::size_t start, std::size_t len, class pack> 
using get_mid_t=type_t<get_mid<start,len,pack>>; 

,現在我們可以將元素分成第一,最後和東西在休息一個元組:

template<int p_first, int p_second, int p_third, int...is> 
class MyClass { 
    using pack = types< int_k<p_first>, int_k<p_second>, int_k<p_third>, int_k<is>... >; 
    OtherClass<p_first> first; 

    using mid = get_mid_t<1, sizeof...(is)+1, pack >; 

    template<class...Ts> 
    using OtherClass_tuple = std::tuple<OtherClass<Ts::value>...>; 

    apply_t< OtherClass_tuple, mid > middle; 

    OtherClass<get_t<sizeof...(is)+2, pack>::value> last; 
}; 
+0

出於興趣,您似乎將所有這些有用的樣板文件保存在某處以便於訪問。你有沒有類似模板資源的公共存儲庫?其他人是否可以將其用於自己的項目/答案? – TartanLlama

+1

@tart我只是輸入它。每次我嘗試寫得更好。第200次寫東西的時候,你已經提出了粗糙的邊緣,希望。 – Yakk

+0

這是一些非常漂亮的通用代碼!似乎是我的問題的答案...測試現在 –

3

由於OtherClass的定義是未知的和矢量不能持有不同類型的OtherClass我假設以下代碼:

該代碼使用遞歸模板繼承與integer_sequence拆分int pack。

#include <utility> 
#include <vector> 

template<int...> 
struct OtherClass { }; 

template<typename, int... > 
struct Splitter; 

template<int... middle, int next, int... rest> 
struct Splitter<std::integer_sequence<int, middle...>, next, rest...> 
    : Splitter<std::integer_sequence<int, middle..., next>, rest...> { }; 

template<int... middle, int last> 
struct Splitter<std::integer_sequence<int, middle...>, last> 
{ 
    static std::vector<int> const& get_vector() 
    { 
     static std::vector<int> const m = { middle... }; 
     return m; 
    } 

    using last_t = std::integral_constant<int, 
     last 
    >; 
}; 

template<int p_first, int p_second, int p_third, int... p_rest> 
class MyClass 
{ 
    OtherClass<p_first> first; 

    using splitter = Splitter<std::integer_sequence<int>, p_second, p_third, p_rest...>; 

    std::vector<int> middle = splitter::get_vector(); 

    typename splitter::last_t last; 
}; 

Demo

+0

對不起,我忘了提及OtherClass的模板(見我的編輯)。雖然在我的情況下這是無法使用的,但這是解決我遇到的另一個問題的完美解決方案:D謝謝 –