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