Reputation: 60441
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?
Upvotes: 0
Views: 871
Reputation: 7904
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...>
Upvotes: 4