Reputation: 3336
When using the C++11 arrow decltype statement, why does boost::optional::operator*()
behave differently than boost::optional::get()
?
Here's my code: (this works)
template<typename Fun, typename... Xs>
auto repeat_remove_optional(Fun f, std::string prompt, Xs&&... xs)
-> decltype(f(prompt, xs...).get())
{
auto x = f(prompt, xs...);
if (x) return *x;
prompt += "(Required!) ";
while (true) {
x = f(prompt, xs...);
if (x) return *x;
}
}
Use case is on some functions that prompt user with string and return boost::none
if they enter escape during the input.
Using -> decltype(*f(prompt, xs...))
won't compile, saying that rvalue reference to type 'bool' cannot bind to lvalue of type 'bool': if (x) return *x;
(at both return statements there is this error).
In other places in my code, the two functions behave identically. Why does this change here?
Usage:
boost::optional<bool> prompt_yes_no(const std::string& message);
bool continue = repeat_remove_optional(prompt_yes_no,
"Are you sure you want to continue?"
Upvotes: 0
Views: 337
Reputation: 7904
operator*
has equivalent semantics with value
, but not with get
.
According to the documentation for boost::optional
, the signatures are:
T const& get() const;
T& get();
T const& operator *() const&;
T& operator *() &;
T&& operator *() &&;
T const& value() const&;
T& value() &;
T&& value() &&;
Under the assumption that f(prompt, xs...)
returns some boost::optional<T>
, decltype(*f(prompt, xs...))
is T&&
whereas decltype(f(prompt, xs...).get())
is T&
.
auto x = f(prompt, xs...); // `x` has type `boost::optional<T>`.
if (x) {
return *x; // `*x` has type `T&`.
}
Substitute bool
for T
and we've got your error of rvalue reference to type 'bool' cannot bind to lvalue of type 'bool'
In this case, return *std::move(x)
would keep the expressions equivalent.
NOTE: return std::move(*x)
would also work, but the subtle semantic difference is that you would be invoking T& operator *() &
then performing std::move
on the resulting T&
as opposed to performing std::move
on boost::optional<T>&
then invoking T&& operator *() &&
. The latter is more accurate, since the expression *f(prompt, xs...)
invokes T&& operator*() &&
.
UPDATE (Thanks Praetorian for the comment): With the return type as specified, you're now returning a T&&
into the contained value of x
which is on the function stack. It would be best to decay
the resulting type also: std::decay_t<decltype(*f(prompt, xs...))>
.
Upvotes: 4