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

2451. [fund.ts.v2] optional<T> should 'forward' T's implicit conversions

Section: 5.3 [fund.ts.v2::optional.object] Status: TS Submitter: Geoffrey Romer Opened: 2014-10-31 Last modified: 2018-07-08

Priority: Not Prioritized

View all other issues in [fund.ts.v2::optional.object].

View all issues with TS status.

Discussion:

Addresses: fund.ts.v2

Code such as the following is currently ill-formed (thanks to STL for the compelling example):

optional<string> opt_str = "meow";

This is because it would require two user-defined conversions (from const char* to string, and from string to optional<string>) where the language permits only one. This is likely to be a surprise and an inconvenience for users.

optional<T> should be implicitly convertible from any U that is implicitly convertible to T. This can be implemented as a non-explicit constructor template optional(U&&), which is enabled via SFINAE only if is_convertible_v<U, T> and is_constructible_v<T, U>, plus any additional conditions needed to avoid ambiguity with other constructors (see N4064, particularly the "Odd" example, for why is_convertible and is_constructible are both needed; thanks to Howard Hinnant for spotting this).

In addition, we may want to support explicit construction from U, which would mean providing a corresponding explicit constructor with a complementary SFINAE condition (this is the single-argument case of the "perfect initialization" pattern described in N4064).

[2015-10, Kona Saturday afternoon]

STL: This has status LEWG, but it should be priority 1, since we cannot ship an IS without this.

TK: We assigned our own priorities to LWG-LEWG issues, but haven't actually processed any issues yet.

MC: This is important.

[2016-02-17, Ville comments and provides concrete wording]

I have prototype-implemented this wording in libstdc++. I didn't edit the copy/move-assignment operator tables into the new operator= templates that take optionals of a different type; there's a drafting note that suggests copying them from the existing tables.

[LEWG: 2016-03, Jacksonville]

Discussion of whether variant supports this. We think it does.

Take it for C++17.

Unanimous yes.

Proposed resolution:

This wording is relative to N4562.

  1. Edit 22.5.3 [optional.optional] as indicated:

    template <class T>
    class optional
    {
    public:
      typedef T value_type;
      
      // 5.3.1, Constructors
      constexpr optional() noexcept;
      constexpr optional(nullopt_t) noexcept;
      optional(const optional&);
      optional(optional&&) noexcept(see below);
      constexpr optional(const T&);
      constexpr optional(T&&);
      template <class... Args> constexpr explicit optional(in_place_t, Args&&...);
      template <class U, class... Args>
        constexpr explicit optional(in_place_t, initializer_list<U>, Args&&...);
      
      
      […]
      
      // 5.3.3, Assignment
      optional& operator=(nullopt_t) noexcept;
      optional& operator=(const optional&);
      optional& operator=(optional&&) noexcept(see below);
      template <class U> optional& operator=(U&&);
      
      template <class... Args> void emplace(Args&&...);
      template <class U, class... Args>
        void emplace(initializer_list<U>, Args&&...);
    
      […]
      
    };
    
  2. In 5.3.1 [fund.ts.v2::optional.object.ctor], insert new signature specifications after p33:

    
    

    
    

    
    

  3. In 5.3.3 [fund.ts.v2::optional.object.assign], change as indicated:

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

    -22- Remarks: If any exception is thrown, the result of the expression bool(*this) remains unchanged. If an exception is thrown during the call to T's constructor, the state of v is determined by the exception safety guarantee of T's constructor. If an exception is thrown during the call to T's assignment, the state of *val and v is determined by the exception safety guarantee of T's assignment. The function shall not participate in overload resolution unless is_same_v<decay_t<U>, T> is true.

    -23- Notes: The reason for providing such generic assignment and then constraining it so that effectively T == U is to guarantee that assignment of the form o = {} is unambiguous.