Is it possible to initialize all elements of std::tuple
by the same argument, using the non-default constructors of the underlying types?
template <typename... TElements>
struct Container {
// I'd wish to be able to do something like this:
Container(Foo foo, Bar bar)
: tuple(foo, bar)
{}
std::tuple<TElements...> tuple;
};
The point is that I don't know the tuple size (it's templated by a variadic parameter), so I can't duplicate the arguments as many times as I need. The only thing I know is that all types in TElements
have a constructor taking Foo
and Bar
as arguments and don't have a default constructor.
The clearest way is just to construct each element in the tuple
constructor argument list:
template <typename... TElements>
struct Container {
Container(Foo foo, Bar bar)
: tuple(TElements{foo, bar}...)
{}
std::tuple<TElements...> tuple;
};
This will result in move (or copy) constructing each element of the tuple from its corresponding constructor parameter; if this is unacceptable you could use piecewise construction:
template <typename... TElements>
struct Container {
Container(Foo foo, Bar bar)
: tuple(std::piecewise_construct, (sizeof(TElements), std::tie(foo, bar))...)
{}
std::tuple<TElements...> tuple;
};
Unfortunately in this case we have to do some kind of gymnastics (here sizeof
and a comma operator) to get the variadic list TElements
mentioned and ignored.
with double parameter pack expansion you can (try to) construct each element of a given tuple class with all given parameters to a function:
template <class T> struct tuple_construct_t;
template <class... Ts> struct tuple_construct_t<std::tuple<Ts...>> {
template <class... Args>
static std::tuple<Ts...> make_tuple(Args&&... args) {
//this is the central part - the double pack expansion
return std::make_tuple(Ts{args...}...);
}
};
// a little free helper function...
template <class Tup, class... Args>
Tup construct_tuple(Args&&... args) {
return tuple_construct_t<Tup>::make_tuple(std::forward<Args>(args)...);
}
And then somewhere in the code:
typedef std::tuple<NoDefault1, NoDefault2> myTuple;
auto t = construct_tuple<myTuple>(Foo{}, Bar{});
full working example: Link
Edit:
Since @Rakvan deleted his answer, I'll preserve the second (correct) part of it:
template <class ... Ts, class ... Args>
std::tuple<Ts...> cartesian_make_tuple(Args && ... args)
{
return std::make_tuple(Ts{args...}...);
}
here is a working exaple
We want to do variadic expansion (to get just the right amount of parameters), but we have to put a ‘hint’ to tie the expansion to whichever pack it is we want to match:
template<typename Dummy, typename Value>
Value depends(Value&& value)
{ return std::forward<Value>(value); }
template<typename... Elements>
void example()
{
// naive attempt to construct all the elements from 0:
// std::tuple<Elements...> t { 0... };
// now expansion is tied to the Elements pack
std::tuple<Elements...> t { depends<Elements>(0)... };
// with two arguments:
std::tuple<Elements...> t { { depends<Elements>(0), depends<Elements>(1) }... };
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With