Вопрос

Consider the following program:

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

If you try to compile it, the compiler will "explode", throwing a quite impressive error with templates of templates of templates...

My question is quite simple: where is the problem and how to solve it?

Это было полезно?

Решение

If I understand your intention correctly, it looks like what you want is to pass M arguments to a std::tuple<> of size N, where M <= N. If M < N, fill in the arguments that aren't provided with default constructed values of the type.

If that's the case, the constructor for Base should be:

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

So that it will go through the recursive, final, then forward_as_tuple.

Alternative Solution:

NOTE: Implementation of std::integer_sequence and related helpers are omitted. You can refer to the paper N3658 and an implementation 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...>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top