The standard templates std::pair
and std::array
are special cases of std::tuple
, and it stands to reason that they should have a very similar set of capabilities.
However, uniquely among the three, std::pair
allows for piecewise construction. That is, if the types T1
and T2
can be constructed from a set of arguments a1, a2, ...
and b1, b2, ...
, then morally speaking we can make a pair
"pair<T1, T2> p(a1, a2, ..., b1, b2, ...)"
directly. Practically, this is spelt out as something like this:
std::pair<T1, T2> p(std::piecewise_construct,
std::forward_as_tuple(a1, a2, ...),
std::forward_as_tuple(b1, b2, ...));
Question: Why doesn't the same piecewise constructibility exist for arrays and tuples? Is there a profound reason, or is this a plain omission? For example, it would be nice to have:
std::tuple<T1, T2, T3> t(std::piecewise_construct,
std::forward_as_tuple(a1, a2, ...),
std::forward_as_tuple(b1, b2, ...),
std::forward_as_tuple(c1, c2, ...));
Is there a reason this cannot be done? [Edit: Or am I misunderstanding the purpose of piecewise construction entirely?]
(I do really have a situation in which I would like to initialize a vector of tuples with a defaulted element value which I would prefer to construct directly from the arguments, without spelling out each tuple element type again.)
I'm not sure why it's not there. Previously, I thought that the implementation wouldn't be possible, given the current varadic template syntax, but I realized that it can be done if it's broken into pieces.
If they had defined an interface like this:
And made it a requirement that the arguments are something that you can use
std::get<N>
to access the arguments (basically, tuples, pairs, arrays). There would have to be extra checks to verify there isn't a mismatch between the number of arguments given and the number of elements in the tuple.Edit: This problem has been bothering me since I read it. And I've created the following class, it is derived from
std::tuple
, and has no data members, so you can assign it to the tuple and the slicing is harmless. The current version requires that the elements be moveable or copyable, as it creates a temporary and then inserts that into the tuple. If you were a tuple implementer, it should be possible to eliminate even that move.Here is my implementation of tuple piecewise (it also allow to omit values with
omit
"keyword"). Zero overhead (no copy/move - direct construction):http://coliru.stacked-crooked.com/a/6b3f9a5f843362e3
Usage:
Tuple inside tuple (zero overhead):
My recollection is that piecewise construction was added to
std::pair
for one reason only: to support uses-allocator construction of the pair elements, i.e. to allow an allocator to be provided and conditionally passed to the elements if they support construction with an allocator (see [allocator.uses] in the standard).At one point during the C++0x process
std::pair
had twice as many constructors as it does now, with every constructor having a corresponding "allocator-extended" version taking astd::allocator_arg_t
and an allocator argument e.g.There was something of a running joke (haha, only serious) about the insane complexity of
std::pair
. The support for passing allocators to the elements was removed fromstd::pair
and moved intostd::scoped_allocator_adaptor
, which is responsible for detecting whether the elements should be constructed with an allocator (see theconstruct
overloads taking a pointer tostd::pair
in [allocator.adaptor.members]).A nice consequence of the piecewise construction is that you can do "emplace" style initialization of pair elements, allowing pairs of non-movable, non-copyable types, but as far as I know that was not the goal of the design.
So the reason
tuple
doesn't support it is that the feature was invented to simplifypair
which had ballooned from a very simple type in C++03 to a laughing stock in C++0x, but doing the same fortuple
was not considered as important (it was new for C++11 anyway). Also, extendingscoped_allocator_adaptor
to handle tuples of arbitrary numbers of elements would have made that adaptor much more complicated.As for
std::array
, that's an aggregate type (because reasons) so adding a constructor takingpiecewise_construct_t
is not possible without making it a non-aggregate.