Skip to content

[expected.object.obs] Inconsistency between value_or() and error_or() in std::expected<T, E> #7913

@onihusube

Description

@onihusube
Contributor

[expected.object.obs]/21

The return value of value_or() is specified as follows:

Returns: has_value() ? **this : static_cast<T>(std​::​forward<U>(v)).

Meanwhile, the return value of error_or() is specified as follows:

Returns: std​::​forward<G>(e) if has_value() is true, error() otherwise.

Since these functions appear to be dual in nature, it would be preferable to maintain consistent notation. Therefore, should error_or() be modified in this way?

Returns: has_value() ? std​::​forward<G>(e) : error().

There is another problem at this time.

value_or() casts its argument v using static_cast<T>(std::forward<U>(v)). Shouldn't error_or() similarly perform a static_cast<E>?

When the constructor used for conversion is marked as explicit, value_or() will successfully perform the conversion, while error_or() will result in a compilation error.

Here I referred to const & overload, but && overload has the same problem.

Activity

frederick-vs-ja

frederick-vs-ja commented on Jun 5, 2025

@frederick-vs-ja
Contributor

LWG issue seems needed. At least, we should be consistent on implicit or explicit conversion for value_or.

jwakely

jwakely commented on Jun 26, 2025

@jwakely
Member

Since these functions appear to be dual in nature, it would be preferable to maintain consistent notation. Therefore, should error_or() be modified in this way?

This part certainly seems editorial, so in scope for this issue.

The requirement for implicit conversions in error_or has been submitted as an LWG issue.

jwakely

jwakely commented on Jun 26, 2025

@jwakely
Member

When the constructor used for conversion is marked as explicit, value_or() will successfully perform the conversion, while error_or() will result in a compilation error.

But expected::error_or has this requirement:

Mandates: is_copy_constructible_v<E> is true and is_convertible_v<G, E> is true.

So a type which is not implicitly convertible cannot be used there anyway. We could change the Returns: element to use a static_cast but it still wouldn't allow explicit constructors.

tkoeppe

tkoeppe commented on Jun 26, 2025

@tkoeppe
Contributor

Is the rewrite really editorial? As written, error_or can return std​::​forward<G>(e). With the rewrite, the result would have to be converted to the common type, wouldn't it (which could change value categories)?

jwakely

jwakely commented on Jun 26, 2025

@jwakely
Member

That's why the static_cast is there for optional::value_or and expected::value_or. Not only can the common type change value categories, but it might be ill-formed if G is implicitly convertible to E and E is implicit convertible to G.

tkoeppe

tkoeppe commented on Jun 26, 2025

@tkoeppe
Contributor

Ah, but the function returns a prvalue, so it shouldn't matter.

jwakely

jwakely commented on Jun 27, 2025

@jwakely
Member

Tomasz drew my attention to cplusplus/papers#1840 which seems relevant here. The error_or wording seems strictly better than requiring the use of a conditional expression.

If the implementation does:

if (has_value())
  return value();
else
  return g;

then there's an implicit conversion, and no surprises from the condition expression.

Let's handle this as an LWG issue.

jwakely

jwakely commented on Jun 27, 2025

@jwakely
Member
tkoeppe

tkoeppe commented on Jun 27, 2025

@tkoeppe
Contributor

Thanks! Can we close this editorial issue then?

jwakely

jwakely commented on Jun 27, 2025

@jwakely
Member

I already did :)

tkoeppe

tkoeppe commented on Jun 27, 2025

@tkoeppe
Contributor

Hah, indeed, you did!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jwakely@tkoeppe@frederick-vs-ja@onihusube

        Issue actions

          [expected.object.obs] Inconsistency between `value_or()` and `error_or()` in `std::expected<T, E>` · Issue #7913 · cplusplus/draft