7

Assume we have a function which returns std::optional<A>. Then what is a proper way of using the result in the range-based for loop? The easiest way does not work:

for (auto&& e : a().value()) {
                   // ^--- A&& is returned, so A is destructed
                   // before loop starts

This issue would not exist if we would had T optional::value() && instead of T&& optional::value() &&, but both STL and Boost define it in a second way.

What is a proper way of handling this situation? I don't like both solutions that I could think of (sandbox):

std::experimental::optional<A> a() {
  // ...
}

void ok1() {
  // ugly if type of A is huge
  for (auto&& e : A(a().value())) {
     // ...
  }
}

void ok2() {
  // extra variable is not used
  // if for some reason we are sure that we have a value
  // and we skip checks
  auto&& b = a();
  for (auto&& e : b.value()) {
    // ...
  }
}

// it may be that the best choice is to define
A aForced() {
    return A(a().value());
}
ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • I actually wrote a whole post complaining about this exact behavior, and I talk about various options for solving it in user defined types, maybe you'd find it interesting: http://www.nirfriedman.com/2016/01/18/writing-good-cpp-by-default-in-the-stl/. – Nir Friedman Jun 05 '16 at 00:16
  • @NirFriedman I never thought of the same kind of problems with std::get & co, thanks! And FYI: the code I am discussing in this question is live, so your post is really practical. I don't understand why there is no `std::copy_of` as one from the @yakk's answer, at least it would be a good way to make people pay attention to the problem. – Dmitry Panteleev Jun 05 '16 at 00:30

1 Answers1

2

This solves your problem:

template<class T>
std::decay_t<T> copy_of(T&& t){
  return std::forward<T>(t);
}

template<class T, std::size_t N>
void copy_of(T(&)[N])=delete;

Then:

for(auto&& x:copy_of(a().value()))

The copy_of technique generally solves functions returning rvalue references being used on for(:) loops.


An alternative is writing value_or_run(T&&, F&&f) which takes a lambda can also be useful. In the F you can do whatever you want, such as throw, and it returns a T not a T&&.

Similarly, value_or.

My personal optional used emplace syntax for value_or -- if the one you are using has that, then .value_or( throw_if_empty{} ), where throw_if_empty has an operator T() that throws the optional empty error.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    Sometime disengaged optional would mean an error somewhere, and an exception would be just fine. Actually, defining `T extract(optional&& o)` appeals to me most of everything, and it is a little strange that there is nothing standard for that. – Dmitry Panteleev Jun 04 '16 at 23:56
  • @DmitryPanteleev Ah, I forgot `.value()` throws. – Yakk - Adam Nevraumont Jun 05 '16 at 00:14
  • As it was pointed in another comment, this is not-optional-only problem, so `copy_of` could be indeed useful. `throw_if_empty` is cool, but it may be not clear for someone new why is it used instead `.value()` – Dmitry Panteleev Jun 05 '16 at 00:34