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
    std::forward_as_tuple(5) = x; // OK
    // 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&>::valueis 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 theith element of*thiswithstd::forward<Ui>(get<i>(u)).20 Remarks: This constructor shall not participate in overload resolution unless
is_constructible<Ti, Ui&&>::valueis true for alli. The constructor isexplicitif and only ifis_convertible<Ui&&, Ti>::valueis 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!
Aucun commentaire:
Enregistrer un commentaire