Doc. no.: | WG21/N3982 |
---|---|
Date: | 2014-04-03 |
Author: | Andrzej Krzemieński |
Contact: | akrzemi1@gmail.com |
Addresses: | fundamentals-ts |
This document proposes an addition of missing rvalue reference overloads for optional
observer functions.
Consider the following usage of optional
. A converting function that converts from type T
to some other type U
:
template <typename U, typename T> optional<U> convert(const T& v);
Such a conversion may fail because certain values of type T
cannot be represented by any value of type U
. For instance converting a string
to int
may fail. One user can consider such failure an exceptional condition that qualifies for a throw. Other user may just want to use a default value instead. Some other user may want to perform some fallback action, or skip some part of the code. Finally, a different user can just be sure that the conversion will not fail (e.g. when converting an int
to a string
), and may want to skip a time-consuming check. optional
can satisfy all these expectations:
int i; auto oi = convert<int>(str); if (oi) i = *oi; // (1) check and skip logic i = convert<int>(str).value_or(-1); // (2) use default i = convert<int>(str).value(); // (3) throw if empty i = *convert<int>("1"); // (4) unchecked
Now, suppose that, rather than to type int
, we are converting to a type that is MoveConstructible
but not CopyConstructible
. Case (1), with manual check, does not work (unless we put an explicit move
). But that is understandable: we want to perform two reads from the optional object: one to check if it contains the value, the other to read the value. For that we need a named object; it cannot be a temporary, so no implicit moving is possible. Case (2) will work fine. This is because function value_or
has two overloads: for *this
being an lvalue reference to const
and rvalue reference. Cases (3) and (4) will not work although they could have. optional
is just missing the rvalue reference overloads for these functions. This is what this proposal intends to fix: add rvalue reference overloads for member functions value
and operator*
(not for operator->
, because it appears not to be implementable). I consider it a bug fix rather than a new feature, primarily because such an overload already exists and has been approved for function value_or
. This is why I propose to fix it still in the first version of Fundamentals TS.
constexpr
member functions automatically become const
, and one cannot implement a member function that is both constexpr
and has an rvalue reference qualifier. C++14 fixed it though, and a reference implementation of the proposed fix to optional in C++14 (tested in Clang 3.4) can be accessed at https://github.com/akrzemi1/Optional. For consistency, this proposal makes all the observer functions constexpr
.
The insertions and deletions in this section describe the changes to Fundamentals TS, assuming N3966 has been incorporated.
In optional synopsis [optional.synop], change the declaration of observers as follows:
Change [optional.object.observe] as follows:// 5.4.5, observers constexpr T const* operator ->() const; constexpr T* operator ->(); constexpr T const& operator *() const&; constexpr T& operator *() &; constexpr T&& operator *() &&; constexpr explicit operator bool() const noexcept; constexpr T const& value() const&; constexpr T& value() &; constexpr T&& value() &&; template <class U> constexpr T value_or(U&&) const&; template <class U> constexpr T value_or(U&&) &&;
5.4.5 Observers [optional.object.observe]
constexpr T const* operator->() const;
constexpr T* operator->();
- Requires:
bool(*this)
.- 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:
bool(*this)
.- Returns:
*val
.- Throws:
Nothing.
- Remarks:
The first functionThese functions shall beaconstexpr
functions.
constexpr T&& operator*() &&
- Requires:
bool(*this)
.- Returns:
std::move(*val)
.- Throws:
Nothing.
- Remarks:
This function shall be a
constexpr
function.
constexpr explicit operator bool() noexcept;
- Returns:
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() &;
- Returns:
*val
, ifbool(*this)
.- Throws:
bad_optional_access
if!*this
.- Remarks:
The first functionThese functions shall beaconstexpr
functions.
constexpr T && value() &&;
- Returns:
std::move(*val)
, ifbool(*this)
.- Throws:
bad_optional_access
if!*this
.- Remarks:
This function shall be a
constexpr
function.
template <class U> constexpr T value_or(U&& v) const&;
- Requires:
is_copy_constructible<T>::value
istrue
andis_convertible<U&&, T>::value
istrue
.- Returns:
bool(*this) ? **this : static_cast<T>(std::forward<U>(v))
.- Throws:
Any exception thrown by the selected constructor of
T
.- Exception Safety:
If
bool(*this)
and exception is thrown during the call toT
's constructor, the value ofbool(*this)
andv
remains unchanged and the state of*val
is 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*this
remains unchanged and the state ofv
is determined by the exception safety guarantee of the selected constructor ofT
.- Remarks:
If any of the constructors of
T
which could be selected is aconstexpr
constructor, this function shall be aconstexpr
function.
template <class U> constexpr T value_or(U&& v) &&;
- Requires:
is_move_constructible<T>::value
istrue
andis_convertible<U&&, T>::value
istrue
.- 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
bool(*this)
and exception is thrown during the call toT
's constructor, the value ofbool(*this)
andv
remains unchanged and the state of*val
is 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*this
remains unchanged and the state ofv
is determined by the exception safety guarantee of the selected constructor ofT
.- Remarks:
If any of the constructors of
T
which could be selected is aconstexpr
constructor, this function shall be aconstexpr
function.
Daniel Krügler and Tomasz Kamiński reviewed the proposal and suggested fixes.