7

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&&?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Curious
  • 20,870
  • 8
  • 61
  • 146
  • Can't take the credit for other peoples' knowledge, but here's a link: http://www.nirfriedman.com/2016/01/18/writing-good-cpp-by-default-in-the-stl/ In section "Safe Iteration Optional" there is some useful info about where `T&& value()` is used. – iehrlich May 31 '17 at 01:24
  • 2
    It would be unsafe to return a non-const reference to a const object ... and also unsafe to return an lvalue reference to what might be a temporary object... ergo, it returns a const rvalue reference – M.M May 31 '17 at 02:15
  • What would be the alternative you're suggesting? A non-`const` rvalue reference? A `const` lvalue reference? – Nicol Bolas May 31 '17 at 02:29
  • I'm wondering why they needed to add that overload at all instead of just one `const T&` overload. And if they needed to why not just return a `const T&` in that case? Is there some actual use case for that overload? – Curious May 31 '17 at 03:12
  • Possible duplicate of [this](https://stackoverflow.com/q/4938875/2069064)? Ultimately, this question is just: what is the point of `T const&&`. – Barry May 31 '17 at 15:35

1 Answers1

7

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • How can a `const T&&` overload give permission to a function to steal its state? I don't think I follow... – Curious May 31 '17 at 03:11
  • @curious the state is mutable in this case, so const blocks nothing. `&&` means the object is an rvalue or being cast to an rvalue, so the caller promises not to mind if you steal some state. – Yakk - Adam Nevraumont May 31 '17 at 10:07
  • Could you give an example of what you mean? – Curious May 31 '17 at 17:55
  • @Curious toy example added. – Yakk - Adam Nevraumont May 31 '17 at 18:04
  • Won't this example work even if the `value()` method had returned a `const&`? – Curious May 31 '17 at 18:58
  • @Curious No, you should not move from a `const&`, even mutable data. In that case, we'd copy the data. A `const&` is a const lvalue, not a const rvalue. Lvalues may be used later; silently stealing from them is rude. rvalues mean "sure, steal my stuff if you need it". In the case of my code above, it should evaluate to a copy for `const&` and a move for `const&&`. – Yakk - Adam Nevraumont May 31 '17 at 18:59
  • Nevermind, I think I understand, fetching the `mutable` value from the `const&&` yields a `&&` and not a `const&&`. Clever! – Curious May 31 '17 at 19:01
  • Is the `return {};` valid? `test_type`'s default constructor will be deleted, but the `{}` will value-initialize it, which checks the constraints of default initialization. – Johannes Schaub - litb Jun 02 '17 at 20:29
  • @JohannesSchaub-litb What makes `test_type`'s default constructor be deleted? The `const` member? – Yakk - Adam Nevraumont Jun 02 '17 at 20:39
  • @Yakk um, I looked up and it appears that `{}` will prefer aggregate initialization over value initialization. So it seems fine. – Johannes Schaub - litb Jun 02 '17 at 21:27