5

I have had runtime error, when replaced some code by using std::optional:

Old code:

T getValue();
...
const auto& value = getValue();
value.get();

New code:

std::optional<T> getValue();
...
const auto& value = getValue().value();
value.get(); // Runtime error, crash 

It was unpredictable for me. The reason of crash is that the method returns T&&.

My question is in what cases T&& can be useful, why the method does not return a T.

Complete code:

#include <experimental/optional>
#include <iostream>
#include <memory>

struct Value {
    std::unique_ptr<int> a = std::make_unique<int>(5);
};

std::experimental::optional<Value> getValue() {
    Value v;
    return v;
}

int main() {
    const Value& value = getValue().value();
    std::cout << *value.a << std::endl;
    return 0;
}
Draks
  • 323
  • 2
  • 9
  • 5
    Please **[edit]** your question with an [mcve] or [SSCCE (Short, Self Contained, Correct Example)](http://sscce.org) – NathanOliver Sep 14 '17 at 12:34
  • Also, [this](https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope) is probably relevant if you are returning a reference. – NathanOliver Sep 14 '17 at 12:37
  • You would have similar issue if it returns `/*const*/T&` BTW. – Jarod42 Sep 14 '17 at 12:39
  • Returning a `T` would do one extra `move`. – Jarod42 Sep 14 '17 at 12:41
  • In your old code, you assign a temporary T to a T&. I would never do that. Even if it somehow works, it's confusing and doesn't make sense and is just begging for problems. Wouldn't that seem like a dangling reference to you? Even if there's some obscure aspect of the standard which allows it to work, it doesn't make sense. If you want to keep the value around, assign it to a value, not to a reference. I would say the design flaw, if any, in C++ is that your old code works at all. – iPherian Dec 25 '19 at 15:11

2 Answers2

7

It is a minor design flaw caused by two competing needs.

First, avoiding extra moves, and second enabling reference lifetime extension.

These two compete in current C++; you usually cannot solve both problems at once. So you will see code doing one or the other, quite haphazardly.

I personally find returning an rvalue reference to generate more problems than moving from a soon to be destroyed object, but those who standardized std::optional disagreed.

My preferred solution would have other downsides.

To fix this—to not have to make these compromises—we would require a complex messy redefinition of how lifetime extension works. So we have to live with these problems for now.

dfrib
  • 70,367
  • 12
  • 127
  • 192
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

Returning T forces to move construct it, whereas returning (rvalue-)reference has no cost.

Let suppose that you have

std::optional<std::array<T, N>> getOptional();

then

getOptional().value()

would do several copies (RVO doesn't apply here as it would return a (moved) member).

Jarod42
  • 203,559
  • 14
  • 181
  • 302