ISO/IEC JTC1 SC22 WG21 N4078 2014-06-20
Fernando Cacciola, fernando.cacciola@gmail.com Andrzej Krzemieński, akrzemi1@gmail.com Ville Voutilainen, ville.voutilainen@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. In addition, this document includes the feature additions from N3982 and the review feedback from the LWG sessions in the Rapperswil 2014 meeting.
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.4.5, observers constexpr T const* operator ->() const; constexpr T* operator ->(); constexpr T const& operator *() const&; constexpr T& operator *() &; constexpr T operator *() &&; constexpr T operator *() const&&; constexpr explicit operator bool() const noexcept; constexpr T const& value() const&; constexpr T& value() &; constexpr T value() &&; constexpr T value() const&&; template <class U> constexpr T value_or(U&&) const&; template <class U> constexpr T value_or(U&&) &&;
// 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;
Replace [optional.object], para 1 and 2 with the followingprivate:bool init; // exposition onlyT* val; // exposition only };
Any instance of
optional<T>
at any given time either contains a value or does 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 object of typeoptional<T>
is contextually converted tobool
, the conversion returnstrue
if the object contains a value; otherwise the conversion returnsfalse
.
Memberval
is provided for exposition only. When anoptional<T>
object contains a value,val
points to the contained value.
Change [optional.object] subclauses as follows:
5.4.1 Constructors [optional.object.ctor]
constexpr optional() noexcept;
constexpr optional(nullopt_t) noexcept;
- Postconditions:
*this
is disengaged*this does not contain a value
.- Remarks:
No contained value is initialized. For every object type
T
, these constructors shall beconstexpr
constructors (C++11 §7.1.5).
optional(const optional<T>& rhs);
- Requires:
is_copy_constructible<T>::value
istrue
.- Effects:
If
rhs
is engagedcontains a value, initializes the contained value as if direct-non-list-initializing an object of typeT
with the expression*rhs
.- Postconditions:
bool(rhs) == bool(*this)
.- Throws:
Any exception thrown by the selected constructor of
T
.
optional(optional<T> && rhs) noexcept(see below);
- Requires:
is_move_constructible<T>::value
istrue
.- Effects:
If
rhs
is engagedcontains a value, initializes the contained value as if direct-non-list-initializing an object of typeT
with 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
noexcept
is equivalent to:is_nothrow_move_constructible<T>::value
constexpr optional(const T& v);
- Requires:
is_copy_constructible<T>::value
istrue
.- Effects:
Initializes the contained value as if direct-non-list-initializing an object of type
T
with the expressionv
.- Postconditions:
*this
is engagedcontains a value.- Throws:
Any exception thrown by the selected constructor of
T
.- Remarks:
If
T
's selected constructor is aconstexpr
constructor, this constructor shall be aconstexpr
constructor.
constexpr optional(T&& v);
- Requires:
is_move_constructible<T>::value
istrue
.- Effects:
Initializes the contained value as if direct-non-list-initializing an object of type
T
with the expressionstd::move(v)
.- Postconditions:
*this
is engagedcontains a value.- Throws:
Any exception thrown by the selected constructor of
T
.- Remarks:
If
T
's selected constructor is aconstexpr
constructor, this constructor shall be aconstexpr
constructor.
template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);
- Requires:
is_constructible<T, Args&&...>::value
istrue
.- Effects:
Initializes the contained value as if direct-non-list-initializing an object of type
T
with the argumentsstd::forward<Args>(args)...
.- Postconditions:
*this
is engagedcontains a value.- Throws:
Any exception thrown by the selected constructor of
T
.- Remarks:
If
T
's constructor selected for the initialization is aconstexpr
constructor, this constructor shall be aconstexpr
constructor.
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&&...>::value
istrue
.- Effects:
Initializes the contained value as if direct-non-list-initializing an object of type
T
with the argumentsil, std::forward<Args>(args)...
.- Postconditions:
*this
is engagedcontains a value.- 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&&...>::value
istrue
.- Remarks:
If
T
's constructor selected for the initialization is aconstexpr
constructor, this constructor shall be aconstexpr
constructor.5.4.2 Destructor [optional.object.dtor]
~optional();
- Effects:
If
is_trivially_destructible<T>::value != true
and*this
is engagedcontains a value, callsval->T::~T()
.- Remarks:
If
is_trivially_destructible<T>::value == true
then this destructor shall be a trivial destructor.5.4.3 Assignment [optional.object.assign]
optional<T>& operator=(nullopt_t) noexcept;
- Effects:
If
*this
is engagedcontains a value, callsval->T::~T()
to destroy the contained value; otherwise no effect.- Returns:
*this
.- Postconditions:
*this
is disengageddoes not contain a value.
optional<T>& optional<T>::operator=(const optional<T>& rhs);
- Requires:
is_copy_constructible<T>::value
istrue
andis_copy_assignable<T>::value
istrue
.- Effects:
*this
contains a value*this
does not contain a valuerhs
contains a valueassigns *rhs
to the contained valueinitializes the contained value as if direct-non-list-initializing an object of type T
with*rhs
rhs
does not contain a valuedestroys the contained value by calling val->T::~T()
no effect
- If
*this
is disengaged andrhs
is disengaged, no effect, otherwise- if
*this
is engaged andrhs
is disengaged, destroys the contained value by callingval->T::~T()
, otherwise- if
*this
is disengaged andrhs
is engaged, initializes the contained value as if direct-non-list-initializing an object of typeT
with*rhs
, otherwise- (if both
*this
andrhs
are engaged) assigns*rhs
to the contained value.
- Returns:
*this
.- Postconditions:
bool(rhs) == bool(*this)
.- Remarks:
Exception Safety:If any exception is thrown, the result of the expression
valuesbool(*this)
ofremains unchanged. If an exception is thrown during the call toinit
andrhs.init
T
'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>::value
istrue
andis_move_assignable<T>::value
istrue
.- Effects:
The result of the expression
bool(rhs)
remains unchanged.
*this
contains a value*this
does not contain a valuerhs
contains a valueassigns move(*rhs)
to the contained valueinitializes the contained value as if direct-non-list-initializing an object of type T
withmove(*rhs)
rhs
does not contain a valuedestroys the contained value by calling val->T::~T()
no effect
- If
*this
is disengaged andrhs
is disengaged, no effect, otherwise- if
*this
is engaged andrhs
is disengaged, destroys the contained value by callingval->T::~T()
, otherwise- if
*this
is disengaged andrhs
is engaged, initializes the contained value as if direct-non-list-initializing an object of typeT
withstd::move(*rhs)
, otherwise- (if both
*this
andrhs
are engaged) assignsstd::move(*rhs)
to the contained value.
- Returns:
*this
.- Postconditions:
bool(rhs) == bool(*this)
.- Remarks:
The expression inside
noexcept
is equivalent to:is_nothrow_move_assignable<T>::value && is_nothrow_move_constructible<T>::valueException Safety:If any exception is thrown, the result of the expression
valuesbool(*this)
ofremains unchanged. If an exception is thrown during the call toinit
andrhs.init
T
's move constructor, the state of*rhs.val
is determined by theexception safety guarantee ofT
's move constructor. If an exception is thrown during the call toT
's move assignment, the state of*val
and*rhs.val
is determined by the exception safety guarantee ofT
's move assignment.
template <class U> optional<T>& optional<T>::operator=(U&& v);
- Requires:
is_constructible<T, U>::value
istrue
andis_assignable<T&, U>::value
istrue
.- Effects:
If
*this
is engagedcontains a value, assignsstd::forward<U>(v)
to the contained value; otherwise initializes the contained value as if direct-non-list-initializing object of typeT
withstd::forward<U>(v)
.- Returns:
*this
.- Postconditions:
*this
is engagedcontains a value.- Remarks:
Exception Safety:If any exception is thrown, the result of the expression
valuebool(*this)
ofremains unchanged. If an exception is thrown during the call toinit
T
's constructor, the state ofv
is determined by the exception safety guarantee ofT
's constructor. If an exception is thrown during the call toT
's assignment, the state of*val
andv
is determined by the 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
==U
is 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&&...>::value
istrue
.- Effects:
Calls
*this = nullopt
. Then initializes the contained value as ifconstructingdirect-non-list-initializing an object of typeT
with the argumentsstd::forward<Args>(args)...
.- Postconditions:
*this
is engagedcontains a value.- Throws:
Any exception thrown by the selected constructor of
T
.- Remarks:
Exception Safety:If an exception is thrown during the call to
T
's constructor,*this
is 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&&...>::value
istrue
.- Effects:
Calls
*this = nullopt
. Then initializes the contained value as ifconstructingdirect-non-list-initializing an object of typeT
with the argumentsil, std::forward<Args>(args)...
.- Postconditions:
*this
is engagedcontains a value.- Throws:
Any exception thrown by the selected constructor of
T
.- Remarks:
Exception Safety:If an exception is thrown during the call to
T
's constructor,*this
is 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&&...>::value
istrue
.5.4.4 Swap [optional.object.swap]
void optional<T>::swap(optional<T>& rhs) noexcept(see below);
- Requires:
LVvalues of typeTT
shall be swappable andis_move_constructible<T>::value
istrue
.- Effects:
*this
contains a value*this
does not contain a valuerhs
contains a valuecalls swap(*(*this), *rhs)
initializes the contained value of *this
as if direct-non-list-initializing an object of typeT
with the expressionstd::move(*rhs)
, followed byrhs.val->T::~T()
; postcondition is that*this
contains a value andrhs
does not contain a valuerhs
does not contain a valueinitializes the contained value of rhs
as if direct-non-list-initializing an object of typeT
with the expressionstd::move(*(*this))
, followed byval->T::~T()
; postcondition is that*this
does not contain a value andrhs
contains a valueno effect
- If
*this
is disengaged andrhs
is disengaged, no effect, otherwise- if
*this
is engaged andrhs
is disengaged, initializes the contained value ofrhs
by direct-initialization withstd::move(*(*this))
, followed byval->T::~T(), swap(init, rhs.init)
, otherwise- if
*this
is disengaged andrhs
is engaged, initializes the contained value of*this
by direct-initialization withstd::move(*rhs)
, followed byrhs.val->T::~T(), swap(init, rhs.init)
- (if both
*this
andrhs
are engaged) callsswap(*(*this), *rhs)
.
- Throws:
Any exceptions that the expressions in the Effects element
clausethrow.- Remarks:
The expression inside
noexcept
is equivalent to:is_nothrow_move_constructible<T>::value && noexcept(swap(declval<T&>(), declval<T&>()))Exception Safety:If any exception is thrown, the results of the expressions
valuesofinit
andrhs.init
bool(*this)
andbool(rhs)
remain unchanged. If an exception is thrown during the call to functionswap
the state of*val
and*rhs.val
is determined by the exception safety guarantee ofswap
for lvalues ofT
. If an exception is thrown during the call toT
's move constructor, the state of*val
and*rhs.val
is determined by the exception safety guarantee ofT
's move constructor.5.4.5 Observers [optional.object.observe]
constexpr T const* operator->() const;
constexpr T* operator->();
- Requires:
*this
is engagedcontains a value.- Returns:
val
.- Throws:
Nothing.
- Remarks:
Unless
T
is a user-defined type with overloaded unaryoperator&
,the first functionthese functions shall beaconstexpr
functions.
constexpr T const& operator*() const&;
constexpr T& operator*() &;
- Requires:
*this
is engagedcontains a value- Returns:
*val
.- Throws:
Nothing.
- Remarks:
The first functionThese functions shall beaconstexpr
functions.
constexpr T operator*() &&;
constexpr T operator*() const&&;
- Requires:
*this
contains a value- Effects:
equivalent to
return std::move(*val);
- Remarks:
if
is_move_constructible<T>::value
isfalse
, the program is ill-formed.
constexpr explicit operator bool() noexcept;
- Returns:
init
true
if and only if*this
contains a value.- Remarks:
This function shall be a
constexpr
function.
constexpr T const& value() const&;
constexpr T& value() &;
- Effects:
equivalent to
return bool(*this) ? *val : throw bad_optional_access();
Returns:
*val
, ifbool(*this)
.Throws:
bad_optional_access
if!*this
.Remarks:The first function shall be a
constexpr
function.
constexpr T value() &&;
constexpr T value() const&&
- Effects:
equivalent to
return bool(*this) ? std::move(*val) : throw bad_optional_access();
- Remarks:
If
is_move_constructible<T>::value
isfalse
, the program is ill-formed.
template <class U> constexpr T value_or(U&& v) const&;
Requires:
is_copy_constructible<T>::value
istrue
andis_convertible<U&&, T>::value
istrue
.- Effects:
Returns:equivalent to
return bool(*this) ? *val : static_cast<T>(std::forward<U>(v))
.Throws:Any exception thrown by the selected constructor of
T
.Exception Safety:If
init == true
and exception is thrown during the call toT
's constructor, the value ofinit
andv
remains unchanged and the state of*val
is determined by the exception safety guarantee of the selected constructor ofT
. Otherwise, when exception is thrown during the call toT
's constructor, the value of*this
remains unchanged and the state ofv
is determined by the exception safety guarantee of the selected constructor ofT
.Remarks:If both constructors of
T
which could be selected areconstexpr
constructors, this function shall be aconstexpr
function.- Remarks:
If
is_copy_constructible<T>::value && is_convertible<U&&, T>::value
isfalse
, the program is ill-formed.
template <class U> constexpr T value_or(U&& v) &&;
Requires:
is_move_constructible<T>::value
istrue
andis_convertible<U&&, T>::value
istrue
.- Effects:
Returns:equivalent to
return bool(*this) ? std::move(*val) : static_cast<T>(std::forward<U>(v))
.Throws:Any exception thrown by the selected constructor of
T
.Exception Safety:If
init == true
and exception is thrown during the call toT
's constructor, the value ofinit
andv
remains unchanged and the state of*val
is determined by the exception safety guarantee of theT
's constructor. Otherwise, when exception is thrown during the call toT
's constructor, the value of*this
remains unchanged and the state ofv
is determined by the exception safety guarantee of the selected constructor ofT
.- Remarks:
If
is_move_constructible<T>::value && is_convertible<U&&, T>::value
isfalse
, the program is ill-formed.5.5 In-place construction [optional.inplace]
struct in_place_t{};
constexpr in_place_t in_place{};The struct
in_place_t
is an empty structure type used as a unique type to disambiguate constructor and function overloading. Specifically,optional<T>
has a constructor within_place_t
as the first parameterargumentfollowed by a parameteran argumentpack; this indicates thatT
should be constructed in-place (as if by a call to a placement new expression) with the forwarded pack expansionargument packas arguments for the initialization ofT
parameters.5.6
DisengagedNo-value state indicator [optional.nullopt]
struct nullopt_t{see below};
constexpr nullopt_t nullopt(unspecified);The struct
nullopt_t
is an empty structure type used as a unique type to indicatea disengaged statethe state of not containing a value foroptional
objects. In particular,optional<T>
has a constructor withnullopt_t
as a single argument; this indicates that andisengagedoptional object not containing a value shall be constructed.Type
nullopt_t
shall not have a default constructor. It shall be a literal type. Constantnullopt
shall 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);bad_optional_access(); }; }The class
bad_optional_access
defines 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();
- Effects:
Constructs an object of class
bad_optional_access
.- Postcondition:
what()
returns an implementation-defined NTBS.
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:
T
shall meet the requirements ofEqualityComparable
.- Returns:
If
bool(x) != bool(y)
,false
; otherwise ifbool(x) == false
,true
; otherwise*x == *y
.- Remarks:
InstantiationsSpecializations of this function template, for which*x == *y
is a core constant expression, shall beconstexpr
functions.
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 < *y
shall be well-formed and its result shall be convertible tobool
.- Returns:
If
,
(!y)false
; otherwise, if,
(!x)true
; otherwise*x < *y
.- Remarks:
InstantiationsSpecializations of this function template, for whichexpression*x < *y
is a core constant expression, shall beconstexpr
functions.
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.