std::get 对右值引用元组的行为是否危险?
Is the behaviour of std::get on rvalue-reference tuples dangerous?
以下代码:
#include <tuple>
int main ()
{
auto f = [] () -> decltype (auto)
{
return std::get<0> (std::make_tuple (0));
};
return f ();
}
(静默地)生成具有未定义行为的代码 - make_tuple
编辑的临时右值 return 通过 std::get<> 和 decltype(auto) 传播到 return类型。所以它最终 return 引用了一个超出范围的临时文件。在这里查看 https://godbolt.org/g/X1UhSw。
现在,您可能会争辩说我对 decltype(auto)
的使用有问题。但是在我的通用代码中(元组的类型可能是 std::tuple<Foo &>
),我不想总是复制。我真的很想从元组中提取确切的值或引用。
我的感觉是 std::get
的重载很危险:
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get( tuple<Types...>&& t ) noexcept;
虽然将左值引用传播到元组元素上可能是明智的,但我认为这不适用于右值引用。
我相信标准委员会非常仔细地考虑了这一点,但谁能向我解释为什么这被认为是最佳选择?
考虑以下示例:
void consume(foo&&);
template <typename Tuple>
void consume_tuple_first(Tuple&& t)
{
consume(std::get<0>(std::forward<Tuple>(t)));
}
int main()
{
consume_tuple_first(std::tuple{foo{}});
}
在这种情况下,我们知道 std::tuple{foo{}}
是临时的,它将在 consume_tuple_first(std::tuple{foo{}})
表达式的整个持续时间内存在。
我们想避免任何不必要的复制和移动,但仍将 foo{}
的临时性传播到 consume
。
唯一的方法是在使用临时 std::tuple
实例调用时让 std::get
return 一个 右值引用 。
将 std::get<0>(std::forward<Tuple>(t))
更改为 std::get<0>(t)
会产生编译错误(正如预期的那样)(on wandbox)。
有一个 get
替代方案 returns 按价值导致额外的不必要的移动:
template <typename Tuple>
auto myget(Tuple&& t)
{
return std::get<0>(std::forward<Tuple>(t));
}
template <typename Tuple>
void consume_tuple_first(Tuple&& t)
{
consume(myget(std::forward<Tuple>(t)));
}
but can anyone explain to me why this was considered the best option?
因为它启用了可选的通用代码,可以在访问元组时无缝传播临时 右值引用。按值 returning 的替代方法可能会导致不必要的移动操作。
以下代码:
#include <tuple>
int main ()
{
auto f = [] () -> decltype (auto)
{
return std::get<0> (std::make_tuple (0));
};
return f ();
}
(静默地)生成具有未定义行为的代码 - make_tuple
编辑的临时右值 return 通过 std::get<> 和 decltype(auto) 传播到 return类型。所以它最终 return 引用了一个超出范围的临时文件。在这里查看 https://godbolt.org/g/X1UhSw。
现在,您可能会争辩说我对 decltype(auto)
的使用有问题。但是在我的通用代码中(元组的类型可能是 std::tuple<Foo &>
),我不想总是复制。我真的很想从元组中提取确切的值或引用。
我的感觉是 std::get
的重载很危险:
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get( tuple<Types...>&& t ) noexcept;
虽然将左值引用传播到元组元素上可能是明智的,但我认为这不适用于右值引用。
我相信标准委员会非常仔细地考虑了这一点,但谁能向我解释为什么这被认为是最佳选择?
考虑以下示例:
void consume(foo&&);
template <typename Tuple>
void consume_tuple_first(Tuple&& t)
{
consume(std::get<0>(std::forward<Tuple>(t)));
}
int main()
{
consume_tuple_first(std::tuple{foo{}});
}
在这种情况下,我们知道 std::tuple{foo{}}
是临时的,它将在 consume_tuple_first(std::tuple{foo{}})
表达式的整个持续时间内存在。
我们想避免任何不必要的复制和移动,但仍将 foo{}
的临时性传播到 consume
。
唯一的方法是在使用临时 std::tuple
实例调用时让 std::get
return 一个 右值引用 。
将 std::get<0>(std::forward<Tuple>(t))
更改为 std::get<0>(t)
会产生编译错误(正如预期的那样)(on wandbox)。
有一个 get
替代方案 returns 按价值导致额外的不必要的移动:
template <typename Tuple>
auto myget(Tuple&& t)
{
return std::get<0>(std::forward<Tuple>(t));
}
template <typename Tuple>
void consume_tuple_first(Tuple&& t)
{
consume(myget(std::forward<Tuple>(t)));
}
but can anyone explain to me why this was considered the best option?
因为它启用了可选的通用代码,可以在访问元组时无缝传播临时 右值引用。按值 returning 的替代方法可能会导致不必要的移动操作。