ISO/IEC JTC1 SC22 WG21 N3966 2014-03-01
Fernando Cacciola, fernando.cacciola@gmail.com Andrzej Krzemieński, akrzemi1@gmail.comProject: Programming Language C++, fundamentals-ts
This document proposes a number of wording fixes, as suggested in Issaquah meeting, to optional objects proposed in N3793. We do not propose any changes in the functionality. We only reworded the standardese, and applied bug fixes:
decay_t in place of the old type trait.value_or shall be costexpr.The insertions and deletions in this section describe the changes to Fundamentals TS after applying the changes from N3793. In other words, the changes are relative to N3793.
Change [optional.synop]:
// 5.5, In-place construction
struct in_place_t{};
constexpr in_place_t in_place{};
// 5.6, DisengagedNo-value state indicator
struct nullopt_t{see below};
constexpr nullopt_t nullopt(unspecified);
// 5.7, class bad_optional_access
class bad_optional_access;
private:
bool init; // exposition only
T* val; // exposition only
};
Replace [optional.object], para 1 and 2 with the following
Any instance of
optional<T>at any given time can either contain a value or not contain a value. When an instance ofoptional<T>contains a value, it means that an object of typeT, referred to as the optional object's contained value, is allocated within the storage of the optional object. Implementations are not permitted to use additional storage, such as dynamic memory, to allocate its contained value. The contained value shall be allocated in a region of theoptional<T>storage suitably aligned for the typeT. When an instance ofoptional<T>contains a value, its contextual conversion toboolreturnstrue; otherwise it returnsfalse.
Membervalis provided for exposition only. When an optional object contains a valuevalpoints to the contained value.
Change [optional.object] subclauses as follows:
5.4.1 Constructors [optional.object.ctor]
constexpr optional<T>::optional() noexcept;
constexpr optional<T>::optional(nullopt_t) noexcept;
- Postconditions:
*thisis disengaged!*this.- Remarks:
No contained value is initialized. For every object type
Tthese constructors shall beconstexprconstructors (C++11 §7.1.5).
optional<T>::optional(const optional<T>& rhs);
- Requires:
is_copy_constructible<T>::valueistrue.- Effects:
If
bool(rhs)is engagedinitializes the contained value as if direct-non-list-initializing an object of typeTwith the expression*rhs.- Postconditions:
bool(rhs) == bool(*this).- Throws:
Any exception thrown by the selected constructor of
T.
optional<T>::optional(optional<T> && rhs) noexcept(see below);
- Requires:
is_move_constructible<T>::valueistrue.- Effects:
If
bool(rhs)is engagedinitializes the contained value as if direct-non-list-initializing an object of typeTwith the expressionstd::move(*rhs).bool(rhs)is unchanged.- Postconditions:
bool(rhs) == bool(*this).- Throws:
Any exception thrown by the selected constructor of
T.- Remarks:
The expression inside
noexceptis equivalent to:is_nothrow_move_constructible<T>::value
constexpr optional<T>::optional(const T& v);
- Requires:
is_copy_constructible<T>::valueistrue.- Effects:
Initializes the contained value as if direct-non-list-initializing an object of type
Twith the expressionv.- Postconditions:
bool(*this)is engaged.- Throws:
Any exception thrown by the selected constructor of
T.- Remarks:
If
T's selected constructor is aconstexprconstructor, this constructor shall be aconstexprconstructor.
constexpr optional<T>::optional(T&& v);
- Requires:
is_move_constructible<T>::valueistrue.- Effects:
Initializes the contained value as if direct-non-list-initializing an object of type
Twith the expressionstd::move(v).- Postconditions:
bool(*this)is engaged.- Throws:
Any exception thrown by the selected constructor of
T.- Remarks:
If
T's selected constructor is aconstexprconstructor, this constructor shall be aconstexprconstructor.
template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);
- Requires:
is_constructible<T, Args&&...>::valueistrue.- Effects:
Initializes the contained value as if direct-non-list-initializing an object of type
Twith the argumentsstd::forward<Args>(args)....- Postconditions:
bool(*this)is engaged.- Throws:
Any exception thrown by the selected constructor of
T.- Remarks:
If
T's constructor selected for the initialization is aconstexprconstructor, this constructor shall be aconstexprconstructor.
template <class U, class... Args>
constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);
- Requires:
is_constructible<T, initializer_list<U>&, Args&&...>::valueistrue.- Effects:
Initializes the contained value as if direct-non-list-initializing an object of type
Twith the argumentsil, std::forward<Args>(args)....- Postconditions:
bool(*this)is engaged.- Throws:
Any exception thrown by the selected constructor of
T.- Remarks:
The function shall not participate in overload resolution unless
is_constructible<T, initializer_list<U>&, Args&&...>::valueistrue.- Remarks:
If
T's constructor selected for the initialization is aconstexprconstructor, this constructor shall be aconstexprconstructor.5.4.2 Destructor [optional.object.dtor]
optional<T>::~optional();
- Effects:
If
is_trivially_destructible<T>::value != trueandbool(*this)is engaged, callsval->T::~T().- Remarks:
If
is_trivially_destructible<T>::value == truethen this destructor shall be a trivial destructor.5.4.3 Assignment [optional.object.assign]
optional<T>& optional<T>::operator=(nullopt_t) noexcept;
- Effects:
If
bool(*this)is engagedcallsval->T::~T()to destroy the contained value; otherwise no effect.- Returns:
*this.- Postconditions:
!*thisis disengaged.
optional<T>& optional<T>::operator=(const optional<T>& rhs);
- Requires:
is_copy_constructible<T>::valueistrueandis_copy_assignable<T>::valueistrue.- Effects:
- If
*thisis disengaged andrhsis disengaged!*this && !rhs, no effect, otherwise- if
*thisis engaged andrhsis disengagedbool(*this) && !rhs, destroys the contained value by callingval->T::~T(), otherwise- if
*thisis disengaged andrhsis engaged!*tis && bool(rhs), initializes the contained value as if direct-non-list-initializing an object of typeTwith*rhs, otherwise- (if
both*thisandrhsare engagedbool(*this) && bool(rhs)) assigns*rhsto the contained value.- Returns:
*this.- Postconditions:
bool(rhs) == bool(*this).- Exception Safety:
If any exception is thrown, the values
bool(*this)andbool(rhs)ofremain unchanged. If an exception is thrown during the call toinitandrhs.initT's copy constructor, no effect. If an exception is thrown during the call toT's copy assignment, the state of its contained value is as defined by the exception safety guarantee ofT's copy assignment.
optional<T>& optional<T>::operator=(optional<T>&& rhs) noexcept(see below);
- Requires:
is_move_constructible<T>::valueistrueandis_move_assignable<T>::valueistrue.- Effects:
- If
*thisis disengaged andrhsis disengaged!*this && !rhs, no effect, otherwise- if
*thisis engaged andrhsis disengagedbool(*this) && !rhs, destroys the contained value by callingval->T::~T(), otherwise- if
*thisis disengaged andrhsis engaged!*tis && bool(rhs), initializes the contained value as if direct-non-list-initializing an object of typeTwithstd::move(*rhs), otherwise- (if
both*thisandrhsare engagedbool(*this) && bool(rhs)) assignsstd::move(*rhs)to the contained value.- Returns:
*this.- Postconditions:
bool(rhs) == bool(*this).- Remarks:
The expression inside
noexceptis equivalent to:is_nothrow_move_assignable<T>::value && is_nothrow_move_constructible<T>::value- Exception Safety:
If any exception is thrown, the values
bool(*this)andbool(rhs)ofremain unchanged. If an exception is thrown during the call toinitandrhs.initT's move constructor, the state of*rhs.valis determined by exception safety guarantee ofT's move constructor. If an exception is thrown during the call toT's move assignment, the state of*valand*rhs.valis determined by exception safety guarantee ofT's move assignment.
template <class U> optional<T>& optional<T>::operator=(U&& v);
- Requires:
is_constructible<T, U>::valueistrueandis_assignable<T&, U>::valueistrue.- Effects:
If
bool(*this)is engagedassignsstd::forward<U>(v)to the contained value; otherwise initializes the contained value as if direct-non-list-initializing object of typeTwithstd::forward<U>(v).- Returns:
*this.- Postconditions:
bool(*this)is engaged.- Exception Safety:
If any exception is thrown, value
bool(*this)ofremains unchanged. If an exception is thrown during the call toinitT's constructor, the state ofvis determined by exception safety guarantee ofT's constructor. If an exception is thrown during the call toT's assignment, the state of*valandvis determined by exception safety guarantee ofT's assignment.- Remarks:
The function shall not participate in overload resolution unless
is_same<istypename decay<U>::typedecay_t<U>, T>::valuetrue.[Note: The reason to provide such generic assignment and then constraining it so that effectively
T==Uis to guarantee that assignment of the formo = {}is unambiguous. —end note]
template <class... Args> void optional<T>::emplace(Args&&... args);
- Requires:
is_constructible<T, Args&&...>::valueistrue.- Effects:
Calls
*this = nullopt. Then initializes the contained value as ifconstructingdirect-non-list-initializing an object of typeTwith the argumentsstd::forward<Args>(args)....- Postconditions:
bool(*this)is engaged.- Throws:
Any exception thrown by the selected constructor of
T.- Exception Safety:
If an exception is thrown during the call to
T's constructor,*thisis disengageddoes not contain a value, and the previous*val(if any) has been destroyed.
template <class U, class... Args> void optional<T>::emplace(initializer_list<U> il, Args&&... args);
- Requires:
is_constructible<T, initializer_list<U>&, Args&&...>::valueistrue.- Effects:
Calls
*this = nullopt. Then initializes the contained value as ifconstructingdirect-non-list-initializing an object of typeTwith the argumentsil, std::forward<Args>(args)....- Postconditions:
bool(*this)is engaged.- Throws:
Any exception thrown by the selected constructor of
T.- Exception Safety:
If an exception is thrown during the call to
T's constructor,*thisis disengageddoes not contain a value, and the previous*val(if any) has been destroyed.- Remarks:
The function shall not participate in overload resolution unless
is_constructible<T, initializer_list<U>&, Args&&...>::valueistrue.5.4.4 Swap [optional.object.swap]
void optional<T>::swap(optional<T>& rhs) noexcept(see below);
- Requires:
LVvalues of type T shall be swappable andis_move_constructible<T>::valueistrue.- Effects:
- If
*thisis disengaged andrhsis disengaged!*this && !rhs, no effect, otherwise- if
*thisis engaged andrhsis disengagedbool(*this) && !rhs, initializes the contained value ofrhsby direct-initializationas if direct-non-list-initializing an object of typeTwith the expressionstd::move(*(*this)), followed byval->T::~T(),, swap(init, rhs.init)*thisdoes not contain a value andrhscontains a value, otherwise- if
*thisis disengaged andrhsis engaged!*tis && bool(rhs), initializes the contained value of*thisby direct-initializationas if direct-non-list-initializing an object of typeTwith the expressionstd::move(*rhs), followed byrhs.val->T::~T(),, swap(init, rhs.init)*thiscontains a value andrhsdoes not contain a value, otherwise- (if
both*thisandrhsare engagedbool(*this) && bool(rhs)) callsswap(*(*this), *rhs).- Throws:
Any exceptions that the expressions in the Effects clause throw.
- Remarks:
The expression inside
noexceptis equivalent to:is_nothrow_move_constructible<T>::value && noexcept(swap(declval<T&>(), declval<T&>()))- Exception Safety:
If any exception is thrown, values
ofinitandrhs.initbool(*this)andbool(rhs)remain unchanged. If an exception is thrown during the call to functionswapthe state of*valand*rhs.valis determined by the exception safety guarantee ofswapfor lvalues ofT. If an exception is thrown during the call toT's move constructor, the state of*valand*rhs.valis determined by the exception safety guarantee ofT's move constructor.5.4.5 Observers [optional.object.observe]
constexpr T const* optional<T>::operator->() const;
T* optional<T>::operator->();
- Requires:
bool(*this)is engaged.- Returns:
val.- Throws:
Nothing.
- Remarks:
Unless
Tis a user-defined type with overloaded unaryoperator&, the first function shall be aconstexprfunction.
constexpr T const& optional<T>::operator*() const;
T& optional<T>::operator*();
- Requires:
bool(*this)is engaged- Returns:
*val.- Throws:
Nothing.
- Remarks:
The first function shall be a
constexprfunction.
constexpr explicit optional<T>::operator bool() noexcept;
- Returns:
inittrueif and only if*thiscontains a value.- Remarks:
This function shall be a
constexprfunction.
constexpr T const& optional<T>::value() const;
T& optional<T>::value();
- Returns:
*val, ifbool(*this).- Throws:
bad_optional_accessif!*this.- Remarks:
The first function shall be a
constexprfunction.
template <class U> constexpr T optional<T>::value_or(U&& v) const&;
- Requires:
is_copy_constructible<T>::valueistrueandis_convertible<U&&, T>::valueistrue.- Returns:
bool(*this) ? **this : static_cast<T>(std::forward<U>(v)).- Throws:
Any exception thrown by the selected constructor of
T.- Exception Safety:
If
init == truebool(*this)and exception is thrown during the call toT's constructor, the value ofinitbool(*this)andvremains unchanged and the state of*valis determined by the exception safety guarantee of the selected constructor ofT. Otherwise, when an exception is thrown during the call toT's constructor, the value of*thisremains unchanged and the state ofvis determined by the exception safety guarantee of the selected constructor ofT.- Remarks:
If
bothany of the constructors ofTwhich could be selectedareis aconstexprconstructors, this function shall be aconstexprfunction.
template <class U> T optional<T>::value_or(U&& v) &&;
- Requires:
is_move_constructible<T>::valueistrueandis_convertible<U&&, T>::valueistrue.- Returns:
bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v)).- Throws:
Any exception thrown by the selected constructor of
T.- Exception Safety:
If
init == truebool(*this)and exception is thrown during the call toT's constructor, the value ofinitbool(*this)andvremains unchanged and the state of*valis determined by the exception safety guarantee of theT's constructor. Otherwise, when an exception is thrown during the call toT's constructor, the value of*thisremains unchanged and the state ofvis determined by the exception safety guarantee of the selected constructor ofT.5.5 In-place construction [optional.inplace]
struct in_place_t{};
constexpr in_place_t in_place{};The struct
in_place_tis an empty structure type used as a unique type to disambiguate constructor and function overloading. Specifically,optional<T>has a constructor within_place_tas the first argument followed by an argument pack; this indicates thatTshould be constructed in-place (as if by a call to placement new expression) with the forwarded argument pack as parameters.5.6
DisengagedNo-value state indicator [optional.nullopt]
struct nullopt_t{see below};
constexpr nullopt_t nullopt(unspecified);The struct
nullopt_tis an empty structure type used as a unique type to indicatea disengaged statethe state of not containing a value foroptionalobjects. In particular,optional<T>has a constructor withnullopt_tas single argument; this indicates that andisengagedoptional object not containing a value shall be constructed.Type
nullopt_tshall not have a default constructor. It shall be a literal type. Constantnulloptshall be initialized with an argument of literal type.5.7 Class
bad_optional_access[optional.bad_optional_access]namespace std { class bad_optional_access : public logic_error { public: explicit bad_optional_access(const string& what_arg); explicit bad_optional_access(const char* what_arg); }; }The class
bad_optional_accessdefines the type of objects thrown as exceptions to report the situation where an attempt is made to access the value of andisengagedoptional object that does not contain a value.
bad_optional_access(const string& what_arg);
- Effects:
Constructs an object of class
bad_optional_access.- Postcondition:
strcmp(what(), what_arg.c_str()) == 0.
bad_optional_access(const char* what_arg);
- Effects:
Constructs an object of class
bad_optional_access.- Postcondition:
strcmp(what(), what_arg) == 0.
Change [optional.relops] as follows:
5.8 Relational operators [optional.relops]
template <class T> constexpr bool operator==(const optional<T>& x, const optional<T>& y);
- Requires:
Tshall meet the requirements ofEqualityComparable.- Returns:
If
bool(x) != bool(y),false; otherwise ifbool(x) == false,true; otherwise*x == *y.- Remarks:
Instantiations of this function template for which
*x == *yis a core constant expression, shall beconstexprfunctions.
template <class T> constexpr bool operator!=(const optional<T>& x, const optional<T>& y);
- Returns:
!(x == y).
template <class T> constexpr bool operator<(const optional<T>& x, const optional<T>& y);
- Requires:
Expression*x < *yshall be well-formed and its result shall be convertible tobool.- Returns:
If
(!y),false; otherwise, if(!x),true; otherwise*x < *y.- Remarks:
Instantiations of this function template for which expression
*x < *yis a core constant expression, shall beconstexprfunctions.
Change [optional.nullops] as follows:
5.9 Comparison with
nullopt[optional.nullops]
template <class T> constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept;
template <class T> constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept;
- Returns:
.(!x)
template <class T> constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept;
template <class T> constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept;
- Returns:
bool(x).
template <class T> constexpr bool operator<(const optional<T>& x, nullopt_t) noexcept;
- Returns:
false.
template <class T> constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept;
- Returns:
bool(x).
template <class T> constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept;
- Returns:
.(!x)
template <class T> constexpr bool operator<=(nullopt_t, const optional<T>& x) noexcept;
- Returns:
true.
template <class T> constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept;
- Returns:
bool(x).
template <class T> constexpr bool operator>(nullopt_t, const optional<T>& x) noexcept;
- Returns:
false.
template <class T> constexpr bool operator>=(const optional<T>& x, nullopt_t) noexcept;
- Returns:
true.
template <class T> constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept;
- Returns:
.(!x)
Jonathan Wakely reviewed the proposal and offered useful suggestions.
Daniel Krügler provided numerous helpful suggestions, corrections and comments on this proposal; in particular he suggested the addition of and reference implementation for "perfect initialization" operations.
Many people from the Boost community, participated in the developement of the Boost.Optional library. Sebastian Redl suggested the usage of function emplace.
Tony Van Eerd offered many useful suggestions and corrections to the proposal.
People in discussion group "ISO C++ Standard - Future Proposals" provided numerous insightful suggestions: Vladimir Batov (who described and supported the perfect forwarding constructor), Nevin Liber, Ville Voutilainen, Richard Smiths, Dave Abrahams, Chris Jefferson, Jeffrey Yasskin, Nikolay Ivchenkov, Matias Capeletto, Olaf van der Spek, Vincent Jacquet, Kazutoshi Satoda, Vicente J. Botet Escriba, Róbert Dávid, Vincent Jacquet, Luc Danton, Greg Marr, and many more.
Joe Gottman suggested the support for hashing some optional objects.
Nicol Bolas suggested to make operator-> conditionally constexpr based on whether T::operator& is overloaded.