Похоже, что std::tuple
, содержащий одну или несколько ссылок, имеет непредвиденное поведение в отношении построения и назначения (особенно для копирования/перемещения и копирования/перемещения). Он отличается от поведения как std::reference_wrapper
(меняет связанный объект), так и структуры с ссылочной переменной-членом (оператор присваивания удален). Это позволяет использовать удобный std::tie
python как несколько возвращаемых значений, но также позволяет явно неправильный код, например, (ссылка здесь):
#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;
}
Стандарт, по-видимому, требует или сильно намекает на это поведение в разделе назначения последнего рабочего проекта (Ti&
будет сбрасываться до lvalue ref):
template <class... UTypes> tuple& operator=(const tuple<UTypes...>& u);
9 Требуется:
sizeof...(Types) == sizeof...(UTypes)
иis_assignable<Ti&, const Ui&>::value
true для всехi
.10 Эффекты: Назначает каждому элементу u соответствующий элемент * этого.
11 Возвращает: * this
Хотя секция построения движения (ish) 20.4.2.1.20
менее понятна (is_constructible<int&, int&&>
возвращает false
):
template <class... UTypes> EXPLICIT constexpr tuple(tuple<UTypes...>&& u);
18 Требуется:
sizeof...(Types) == sizeof...(UTypes)
.19 Эффекты: Для всех
i
конструктор инициализируетi
-й элемент*this
с помощьюstd::forward<Ui>(get<i>(u))
.20 Примечания: Этот конструктор не должен участвовать в разрешении перегрузки, если только
is_constructible<Ti, Ui&&>::value
не подходит для всехi
. Конструкторexplicit
тогда и только тогда, когдаis_convertible<Ui&&, Ti>::value
false для хотя бы одногоi
.
Это не единственные затронутые подразделы.
Вопрос в том, почему это поведение желательно? Кроме того, если в игре есть другие части стандарта, или я не понимаю, объясните, где я ошибся.
Спасибо!