下面的代码:
#include <tuple>
int main ()
{
auto f = [] () -> decltype (auto)
{
return std::get<0> (std::make_tuple (0));
};
return f ();
}
(无声地)生成具有未定义行为的代码--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;
虽然将左值引用传播到元组元素上可能是明智的,但我不认为这适用于右值引用。
我相信标准委员会会非常仔细地考虑过这个问题,但是谁能向我解释一下为什么这被认为是最好的选择?
2条答案
按热度按时间rdlzhqv91#
请考虑以下示例:
在这种情况下,我们知道
std::tuple{foo{}}
是临时的,它将在consume_tuple_first(std::tuple{foo{}})
表达式的整个持续时间内生存。我们希望避免任何不必要的复制和移动,但仍然将
foo{}
的临时性传播到consume
。唯一的方法是当
std::get
被临时的std::tuple
示例调用时,std::get
返回一个 * 右值引用 *。live example on wandbox
将
std::get<0>(std::forward<Tuple>(t))
变更为std::get<0>(t)
会产生编译错误(如预期)(on wandbox)。使用按值返回的
get
替代项会导致额外的不必要移动:live example on wandbox
但谁能向我解释一下为什么这被认为是最好的选择呢?
因为它启用了可选的泛型代码,可以在访问元组时无缝地传播临时 * 右值引用 *。
8tntrjer2#
IMHO this is dangerous and quite sad since it defeats the purpose of the "most important
const
" :Normally, a temporary object lasts only until the end of the full expression in which it appears. However, C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error.
In light of the quote above, for many years C++ programmers have learned that this was OK:
Now, consider this code :
I believe anyone should be forgiven for thinking that line #1 is OK. However, since
std::get
returns a reference to an object that is going to be destroyed,x
is a dangling reference. The code above outputs:which shows that the object that
x
binds to is destroyed beforehello()
is called. Clang gives a warning about the issue but gcc and msvc don't. The same issue happens if (as in the OP) we usestd::tuple
instead ofstd::variant
but, sadly enough, clang doesn't issues a warning for this case.A similar issue happens with
std::optional
and thisvalue
overload:This code , which uses the same
X
above, illustrates the issue:The output is:
Brace yourself for more of the same with C++23's
std::except
and its methodsvalue()
anderror()
:I'd rather pay the price of the move explained in Vittorio Romeo's post . Sure, I can avoid the issue by removing
&
from lines #1 and #2. My point is that the rule for the "most importantconst
" just became more complicated and we need to consider if the expression involvesstd::get
,std::optional::value
,std::expected::value
,std::expected::error
, ...