Is the behavior of std :: get on an rvalue-reference tuple dangerous?

The following code:

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

(Without sound) generates code with undefined behavior - the temporary rvalue returned by make_tuple is propagated through std :: get <> and through decltype (auto) to the return type. Therefore, it returns a link to a temporary one that is beyond the scope. See here https://godbolt.org/g/X1UhSw .

Now you can claim that my use of decltype(auto) is a mistake. But in my general code (where the tuple type can be std::tuple<Foo &> ), I don't want to always make a copy. I really want to extract the exact value or link from the tuple.

I feel this overload of std::get dangerous:

 template< std::size_t I, class... Types > constexpr std::tuple_element_t<I, tuple<Types...> >&& get( tuple<Types...>&& t ) noexcept; 

Although propagating lvalue links to tuple elements is probably reasonable, I don't think this is the case for rvalue links.

I am sure that the standards committee thought about this very carefully, but can someone explain to me why this is considered the best option?

+5
source share
1 answer

Consider the following example:

 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{}}); } 

In this case, we know that std::tuple{foo{}} is temporary and that it will live for the duration of the consume_tuple_first(std::tuple{foo{}}) expression consume_tuple_first(std::tuple{foo{}}) .

We want to avoid unnecessary copy and move, but still extend the foo{} consume to consume .

The only way to do this is: std::get to return the rvalue link when called with a temporary instance of std::tuple .

live example in wandbox


Changing std::get<0>(std::forward<Tuple>(t)) to std::get<0>(t) creates a compilation error (as expected) ( in wandbox ).


Having an alternative get that returns a value leads to additional unnecessary movement:

 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))); } 

live example in wandbox


but can anyone explain to me why this was considered the best option?

Because it includes additional generic code that smoothly distributes rvalue temporary references when accessing tuples. The alternative of returning by value may lead to unnecessary movement actions.

+2
source

Source: https://habr.com/ru/post/1274224/


All Articles