std :: get on rvalue-reference元组的行为是否危险?

Mat*_*t A 7 c++ rvalue-reference c++11 stdtuple c++14

以下代码:

#include <tuple>

int main ()
{
  auto f = [] () -> decltype (auto)
  {
    return std::get<0> (std::make_tuple (0));
  };

  return f ();
}
Run Code Online (Sandbox Code Playgroud)

(静默地)生成具有未定义行为的代码 - 返回的临时rvalue make_tuple通过std :: get <>和decltype(auto)传播到返回类型.因此它最终返回对超出范围的临时引用.请在此处查看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;
Run Code Online (Sandbox Code Playgroud)

虽然将左值引用传播到元组元素可能是明智的,但我认为这不适用于右值引用.

我确信标准委员会非常仔细地考虑了这一点,但任何人都可以向我解释为什么这被认为是最佳选择?

Vit*_*meo 3

考虑以下示例:

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{}});
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们知道这std::tuple{foo{}}是一个临时的,并且它将在表达式的整个持续时间内存在consume_tuple_first(std::tuple{foo{}})

foo{}我们希望避免任何不必要的复制和移动,但仍然传播to的临时性consume

执行此操作的唯一方法是在使用临时实例调用时std::get返回右值引用std::tuple

wandbox 上的实例


更改std::get<0>(std::forward<Tuple>(t))std::get<0>(t)会产生编译错误(如预期)(在 wandbox 上)。


有一个get按值返回的替代方案会导致额外的不必要的移动:

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)));
}
Run Code Online (Sandbox Code Playgroud)

wandbox 上的实例


但谁能向我解释为什么这被认为是最好的选择?

因为它支持可选的通用代码,在访问元组时无缝传播临时右值引用。按值返回的替代方案可能会导致不必要的移动操作。