Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does std::optional::value_or() take a U&& rather than T&&?

On cppreference, we can see std::optional takes a default value of U&& rather than T&&.

It makes me incapable of writing the following code:

std::optional<std::pair<int, int>> opt;
opt.value_or({1, 2}); // does not compile
opt.value_or(std::make_pair(1, 2)); // compiles

However, I find there is no benefit to using U&&, since U must be convertible to T here.

So, consider the following code, if we have some type U which differs from T, then there will be no perfect match. However, by performing an implicit cast, we can still resolve our call:

template< class U >
constexpr T value_or( T&& default_value ) const&;

I have the following code to test if a template function can take an argument which needs an extra implicit casting to make a perfect match, and it compiles:

#include <cstdio>
#include <optional>
#include <map>

struct A {
    int i = 1;
};

struct B {
    operator A() const {
        return A{2};
    }
};

template <typename T>
struct C {
    void f(T && x) {
        printf("%d\n", x.i);
    }
};

int main() {
    auto b = B();
    C<A> c;
    c.f(b);
}
like image 674
calvin Avatar asked Nov 18 '25 04:11

calvin


2 Answers

I find there it no benefit by using U&&, since U must be convertible to T here.

U must be convertible to T, but a conversion can be expensive. Taking a forwarding reference (U&&) avoids performing that conversion if the argument is not used (if the object contains a value).

The same cppreference page says value_or is equivalent to:
bool(*this) ? **this : static_cast<T>(std::forward<U>(default_value)).
Here, static_cast<T> performs the conversion, but it is only performed if bool(*this) is false.

like image 145
Nelfeal Avatar answered Nov 20 '25 18:11

Nelfeal


It is to allow perfect forwarding. If the signature were

constexpr T value_or( T&& default_value ) const&;

then T is not something that will be deduced, it is known from the class instantiation, meaning T&& default_value is just a plain rvalue reference to whatever T is.

By using

template< class U >
constexpr T value_or( U&& default_value ) const&;

The U needs to be deduced making it a forwarding reference.

like image 21
NathanOliver Avatar answered Nov 20 '25 17:11

NathanOliver



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!