Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the const rvalue qualified std::optional::value() return a const rvalue reference?

std::optional::value() has the following two overloads

constexpr T& value() &;
constexpr const T & value() const &; 
constexpr T&& value() &&;
constexpr const T&& value() const &&;

What is the point of returning a const rvalue reference?

The only reason I can think of is to enable the compiler to help catch undefined behavior in (really really weird) cases like the following

auto r = std::cref(const_cast<const std::optional<int>&&>(
    std::optional<int>{}).value());

Where if the std::optional::value() had returned a const T& then the above code would compile and would lead to undefined behavior when the r reference_wrapper was used later.

Is there any other corner case in mind with the above returning a const T&&?

like image 346
Curious Avatar asked May 31 '17 01:05

Curious


People also ask

What are rvalue references?

Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.

Can a function return an rvalue?

Typically rvalues are temporary objects that exist on the stack as the result of a function call or other operation. Returning a value from a function will turn that value into an rvalue. Once you call return on an object, the name of the object does not exist anymore (it goes out of scope), so it becomes an rvalue.

Can rvalue be const?

So an rvalue can be used both with rvalue overloads and a const lvalue reference.


1 Answers

Sure. You have a const optional<T> in struct. You return an rvalue instance and access the optional member.

Because of how you constructed it, you can guarantee the optional is engaged in this case. So you call value(). The type T contains mutable state that can be efficiently be reused/stolen. The T const&& overload gives the consuming function permission to steal that state.

struct mutable_type {
  mutable std::vector<char> cache;
};
struct test_type {
  const std::optional<mutable_type> bob;
};
test_type factory( int x ) {
  if (x==0) return {};
  return {mutable_type({{1,2,x}})};
}

auto moved_into = factory(3).bob.value().cache;

This, I believe, moves the vector within bob, which is a const rvalue in this context. It relies on value() returning a const&& in a const&& context.

like image 153
Yakk - Adam Nevraumont Avatar answered Nov 14 '22 22:11

Yakk - Adam Nevraumont