This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

3886. Monad mo' problems

Section: 22.5.3.1 [optional.optional.general], 22.8.6.1 [expected.object.general] Status: New Submitter: Casey Carter Opened: 2023-02-13 Last modified: 2023-03-22

Priority: 3

View all other issues in [optional.optional.general].

View all issues with New status.

Discussion:

While implementing P2505R5 "Monadic Functions for std::expected" we found it odd that the template type parameter for the assignment operator that accepts an argument by forwarding reference is defaulted, but the template type parameter for value_or is not. For consistency, it would seem that meow.value_or(woof) should accept the same arguments woof as does meow = woof, even when those arguments are braced-initializers.

That said, it would be peculiar to default the template type parameter of value_or to T instead of remove_cv_t<T>. For expected<const vector<int>, int> meow{unexpect, 42};, for example, meow.value_or({1, 2, 3}) would create a temporary const vector<int> for the argument and return a copy of that argument. Were the default template argument instead remove_cv_t<T>, meow.value_or({1, 2, 3}) could move construct its return value from the argument vector<int>. For the same reason, the constructor that accepts a forwarding reference with a default template argument of T should default that argument to remove_cv_t<T>.

For consistency, it would be best to default the template argument of the perfect-forwarding construct, perfect-forwarding assignment operator, and value_or to remove_cv_t<T>. Since all of the arguments presented apply equally to optional, we believe optional should be changed consistently with expected. MSVCSTL has prototyped these changes successfully.

[2023-03-22; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4928.

  1. Modify 22.5.3.1 [optional.optional.general] as indicated:

    namespace std {
      template<class T>
      class optional {
      public:
        […]
        template<class U = remove_cv_t<T>>
          constexpr explicit(see below) optional(U&&);
        […]
        template<class U = remove_cv_t<T>> constexpr optional& operator=(U&&);
        […]
        template<class U = remove_cv_t<T>> constexpr T value_or(U&&) const &;
        template<class U = remove_cv_t<T>> constexpr T value_or(U&&) &&;
        […]
      };
      […]
    }
    
  2. Modify 22.5.3.2 [optional.ctor] as indicated:

    template<class U = remove_cv_t<T>> constexpr explicit(see below) optional(U&& v);
    

    -23- Constraints: […]

  3. Modify 22.5.3.4 [optional.assign] as indicated:

    template<class U = remove_cv_t<T>> constexpr optional& operator=(U&& v);
    

    -12- Constraints: […]

  4. Modify 22.5.3.6 [optional.observe] as indicated:

    template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) const &;
    

    -15- Mandates: […]

    […]

    template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) &&;
    

    -17- Mandates: […]

  5. Modify 22.8.6.1 [expected.object.general] as indicated:

    namespace std {
      template<class T, class E>
      class expected {
      public:
        […]
        template<class U = remove_cv_t<T>>
          constexpr explicit(see below) expected(U&& v);
        […]
        template<class U = remove_cv_t<T>> constexpr expected& operator=(U&&);
        […]
        template<class U = remove_cv_t<T>> constexpr T value_or(U&&) const &;
        template<class U = remove_cv_t<T>> constexpr T value_or(U&&) &&;
        […]
      };
      […]
    }
    
  6. Modify 22.8.6.2 [expected.object.cons] as indicated:

    template<class U = remove_cv_t<T>>
      constexpr explicit(!is_convertible_v<U, T>) expected(U&& v);
    

    -23- Constraints: […]

  7. Modify 22.8.6.4 [expected.object.assign] as indicated:

    template<class U = remove_cv_t<T>>
      constexpr expected& operator=(U&& v);
    

    -9- Constraints: […]

  8. Modify 22.8.6.6 [expected.object.obs] as indicated:

    template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) const &;
    

    -16- Mandates: […]

    […]

    template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) &&;
    

    -18- Mandates: […]