Of course; there is a fancy and easy way.
A fancy way, which I will describe below. First a simple way: take by value.
template <typename... Args> struct foo<std::tuple<Args...>> { std::tuple<Args...> t; foo(Args... args): t{std::forward<Args>(args)...} { } };
Really, just do it. Forward is used to work correctly if Args contains a link.
Taking by value adds one step over perfect forwarding, but reduces the need for congestion exponentially.
This is a fancy way. We print the erase design:
template<class T> struct make_it { using maker=T(*)(void*); maker f; void* args; // make from move make_it( T&& t ): f([](void* pvoid)->T{ return std::move(*static_cast<T*>(pvoid)); }), args(std::addressof(t)) {} // make from copy make_it( T const& t ): f([](void* pvoid)->T{ return *(T const*)(pvoid); }), args(std::addressof(t)) {} operator T()&&{return std::move(*this)();} T operator()()&&{ return f(args); } };
This type erases the design by copying or moving.
template <typename... Args> struct foo<std::tuple<Args...>> { std::tuple<Args...> t; foo(make_it<Args>... args): t{std::move(args)()...} { } };
It is not completely transparent, but it is as close as possible.
Double {{}} is required instead of single. This is a custom conversion, so no other will be done. We could add a universal ctor: '
// make from universal template<class U> make_it( U&& u ): f([](void* pvoid)->T{ return std::forward<U>(*(U*)(pvoid)); }), args(std::addressof(u)) {}
which works better if we add sfinae teat that U&& can be used to implicitly build T
This has several advantages, but they are marginal, only taking on meaning. For example, in C ++ 17, immovable types can be ideally developed in some cases.