2013-12-12 42 views
0

考慮下面的程序:元編程和SFINAE /的std :: enable_if:無限遞歸模板

// Include 
#include <iostream> 
#include <type_traits> 
#include <utility> 
#include <tuple> 
#include <string> 

// Base class 
template <class Crtp, class... Types> 
struct Base 
{ 
    // Constructor calling the transmute function 
    template <class... OtherTypes> 
    explicit inline Base(const OtherTypes&... source) 
    : _data(transmute<std::tuple<Types...>>(std::forward_as_tuple(source...))) 
    {;} 

    // Transmute: create a new object 
    template <class Output> 
    static constexpr Output transmute() 
    {return Output();} 

    // Transmute: forward existing object 
    template <class Output, 
       class Input, 
       class = typename std::enable_if< 
          std::is_convertible< 
           typename std::remove_cv<typename std::remove_reference<Input>::type>::type, 
           typename std::remove_cv<typename std::remove_reference<Output>::type>::type 
          >::value 
         >::type> 
    static constexpr Input transmute(Input&& input) 
    {return std::forward<Input>(input);} 

    // Transmute: recursive transmutation 
    template <class Output, 
       class... Input, 
       class = typename std::enable_if< 
          (sizeof...(Input) <= std::tuple_size<Output>::value) 
          && (sizeof...(Input) != std::tuple_size<Output>::value) 
         >::type> 
    static constexpr Output transmute(const Input&... input) 
    {return transmute<Output>(input..., typename std::tuple_element<sizeof...(Input), Output>::type());} 

    // Transmute: final step 
    template <class Output, 
       class... Input, 
       class = typename std::enable_if< 
          (sizeof...(Input) == std::tuple_size<Output>::value) 
          && (sizeof...(Input) != 0) 
         >::type> 
    static constexpr Output transmute(Input&&... input) 
    {return transmute<Output>(std::forward_as_tuple(std::forward<Input>(input)...));} 

    // Data member 
    std::tuple<Types...> _data; 
}; 

// Derived class 
struct Derived 
: public Base<Derived, std::string, bool> 
{ 
    // Universal reference constructor 
    template <class... Misc> 
    explicit inline Derived(Misc&&... misc) 
    : Base<Derived, std::string, bool>(std::forward<Misc>(misc)...) 
    {;} 
}; 

// Main 
int main(int argc, char* argv[]) 
{ 
    Derived a("hello"); // Boom !!!! 
    return 0; 
} 

如果您嘗試編譯,編譯器將「爆炸」,拋出一個相當可觀的錯誤與模板模板模板...

我的問題很簡單:問題在哪裏以及如何解決?

+0

因此類似於構成整個程序和輸入,並說它在運行時崩潰。哪裏出了問題?'做更少的事情。測試每個組件。發現從測試和工作到測試和失敗的變化。包括關於你想要做什麼的豐富文檔。嘗試簡化併產生相同的錯誤,減少死刑。 – Yakk

+1

'(sizeof ...(Input)<= std :: tuple_size :: value) &&(sizeof ...(Input)!= std :: tuple_size :: value)'這看起來很可疑。 – dyp

+1

':_data(transmute >(source ...))'並且它編譯(刪除了'std :: forward_as_tuple')。不知道它是否正確。但據我瞭解你的元編程,作爲元組的轉發是不正確的:最後一步期望所有參數* not *被包裝在一個元組中;在你當前的代碼中,''hello''創建的'std :: tuple '不能轉換爲'std :: string',因此轉發永遠不會發生。 – dyp

回答

4

如果我正確理解你的意圖,它看起來像你想要的是通過M參數爲std::tuple<>大小N的,其中M <= N。如果M < N,請填寫未使用該類型的默認構造值提供的參數。

如果是這樣的情況下,爲Base的構造應該是:

template <class... OtherTypes> 
explicit inline Base(const OtherTypes&... source) 
: _data(transmute<std::tuple<Types...>>(source...)) 
{;} 

這樣它會經過遞歸,最終,然後forward_as_tuple

替代解決方案:

注:std::integer_sequence及相關助手實施省略。您可以參考論文N3658和實施here

template <typename T, typename Seq, T Begin> 
struct make_integer_range_impl; 

template <typename T, T... Ints, T Begin> 
struct make_integer_range_impl<T, std::integer_sequence<T, Ints...>, Begin> 
    : public std::common_type<std::integer_sequence<T, Begin + Ints...>> {}; 

/* Similar to std::make_integer_sequence<>, except it goes from [Begin, End) 
    instead of [0, Size). */ 
template <typename T, T Begin, T End> 
using make_integer_range = typename make_integer_range_impl< 
    T, std::make_integer_sequence<T, End - Begin>, Begin>::type; 

/* Similar to std::make_index_sequence<>, except it goes from [Begin, End) 
    instead of [0, Size). */ 
template <std::size_t Begin, std::size_t End> 
using make_index_range = make_integer_range<std::size_t, Begin, End>; 

/* Trivial wrapper for std::tuple_element<>. */ 
template <std::size_t Idx, typename Tuple> 
using tuple_element_t = typename std::tuple_element<Idx, Tuple>::type; 

template <typename... Args> 
class Partial { 
    public: 

    /* Our tuple type. */ 
    using Tuple = std::tuple<Args...>; 

    /* Create an index_range, [# of arguments, tuple size), 
    and forward the arguments to the delegating constructor. */ 
    template <typename... ForwardArgs> 
    Partial(ForwardArgs &&... forward_args) 
     : Partial(make_index_range<sizeof...(ForwardArgs), 
           std::tuple_size<Tuple>::value>(), 
       std::forward<ForwardArgs>(forward_args)...) {} 

    private: 

    /* The given indices are for the missing arguments, get the corresponding 
    types out of the tuple and default construct them. */ 
    template <std::size_t... Indices, typename... ForwardArgs> 
    Partial(std::index_sequence<Indices...> &&, ForwardArgs &&... forward_args) 
     : tuple_{std::forward<ForwardArgs>(forward_args)..., 
       tuple_element_t<Indices, Tuple>{}...} {} 

    /* Our tuple instance. */ 
    Tuple tuple_; 

}; // Partial<Args...>