元编程和SFINAE / std :: enable_if:无限模板递归

时间:2013-12-12 23:02:27

标签: c++ c++11 metaprogramming template-meta-programming sfinae

考虑以下计划:

// 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;
}

如果你试图编译它,编译器将“爆炸”,用模板模板模板抛出令人印象深刻的错误......

我的问题很简单:问题在哪里以及如何解决?

1 个答案:

答案 0 :(得分:4)

如果我理解你的意图,看起来你想要的是将M个参数传递给大小为std::tuple<>的{​​{1}},N。如果M <= N,请填写未提供类型的默认构造值的参数。

如果是这种情况,M < N的构造函数应为:

Base

这样它将通过递归,最终,然后template <class... OtherTypes> explicit inline Base(const OtherTypes&... source) : _data(transmute<std::tuple<Types...>>(source...)) {;}

替代解决方案:

注意: forward_as_tuple及相关助手的实施将被省略。您可以参考论文N3658和实施here

std::integer_sequence