It seems like a std::tuple
containing one or more references has unexpected behavior with regards to construction and assignment (especially copy/move construction and copy/move assignment). It's different from the behavior of both std::reference_wrapper
(changes the referred to object) and a struct with a member reference variable (assignment operator deleted). It allows for the convenient std::tie
python like multiple return values, but it also allows obviously incorrect code like the following (link here):
#include <tuple>
int main()
{
std::tuple<int&> x{std::forward_as_tuple(9)}; // OK - doesn't seem like it should be
std::forward_as_tuple(5) = x; // OK - doesn't seem like it should be
// std::get<0>(std::forward_as_tuple(5)) = std::get<0>(x); // ERROR - and should be
return 0;
}
The standard seems to require or strongly hint at this behavior in the copy(ish) assignment section 20.4.2.2.9
of the latest working draft (Ti&
will collapse to lvalue ref):
template <class... UTypes> tuple& operator=(const tuple<UTypes...>& u);
9 Requires:
sizeof...(Types) == sizeof...(UTypes)
andis_assignable<Ti&, const Ui&>::value
is true for alli
.10 Effects: Assigns each element of u to the corresponding element of *this.
11 Returns: *this
Although the move(ish) construction section 20.4.2.1.20
is less clear (is_constructible<int&, int&&>
returns false
):
template <class... UTypes> EXPLICIT constexpr tuple(tuple<UTypes...>&& u);
18 Requires:
sizeof...(Types) == sizeof...(UTypes)
.19 Effects: For all
i
, the constructor initializes thei
th element of*this
withstd::forward<Ui>(get<i>(u))
.20 Remarks: This constructor shall not participate in overload resolution unless
is_constructible<Ti, Ui&&>::value
is true for alli
. The constructor isexplicit
if and only ifis_convertible<Ui&&, Ti>::value
is false for at least onei
.
These are not the only affected subsections.
The question is, why is this behavior desired? Also, if there are other parts of the standard at play, or I'm misunderstanding it, explain where I went wrong.
Thanks!
std::tuple
containing rvalue references should not support assignment (though the standard doesn't mention this), andstd::tuple<LVALUEREF>
construction fromstd::tuple<RVALUEREF>
might be a bug in libc++. Any input would be valuable, as I'm implementing a tuple variant. – Praxis