From 3df23370e6f94f3954dfd07aa8e184a187ba1dc4 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sat, 7 Feb 2026 02:12:24 +0100 Subject: [PATCH] make optional constexpr in C++14 --- doc/00_optional.qbk | 2 +- doc/01_quick_start.qbk | 1 - doc/17_in_place_factories.qbk | 2 + doc/27_ref_optional_synopsis.qbk | 82 ++- doc/28_ref_optional_semantics.qbk | 117 ++-- doc/90_dependencies.qbk | 15 +- doc/92_relnotes.qbk | 17 +- .../optional/detail/constexpr_optional.hpp | 571 ++++++++++++++++++ .../optional/detail/optional_common_defs.hpp | 180 ++++++ .../detail/optional_factory_support.hpp | 6 - .../boost/optional/detail/optional_hash.hpp | 2 +- .../detail/optional_nonmember_interface.hpp | 117 ++++ .../detail/optional_reference_spec.hpp | 166 ++--- .../boost/optional/detail/optional_relops.hpp | 78 +-- .../boost/optional/detail/optional_swap.hpp | 2 +- .../optional_trivially_copyable_base.hpp | 16 +- .../optional/detail/optional_utility.hpp | 41 -- include/boost/optional/optional.hpp | 334 ++++------ include/boost/optional/optional_fwd.hpp | 7 +- include/boost/optional/optional_io.hpp | 2 +- test/Jamfile.v2 | 1 + test/optional_test_constexpr.cpp | 96 +++ test/optional_test_convert_assign.cpp | 1 + test/optional_test_move.cpp | 72 +-- test/optional_test_path_assignment.cpp | 3 + test/optional_test_sfinae_friendly_ctor.cpp | 3 +- test/optional_test_static_properties.cpp | 4 + test/optional_test_swap.cpp | 11 +- 28 files changed, 1405 insertions(+), 544 deletions(-) create mode 100644 include/boost/optional/detail/constexpr_optional.hpp create mode 100644 include/boost/optional/detail/optional_common_defs.hpp create mode 100644 include/boost/optional/detail/optional_nonmember_interface.hpp delete mode 100644 include/boost/optional/detail/optional_utility.hpp create mode 100644 test/optional_test_constexpr.cpp diff --git a/doc/00_optional.qbk b/doc/00_optional.qbk index 4809c28..53459c9 100644 --- a/doc/00_optional.qbk +++ b/doc/00_optional.qbk @@ -2,7 +2,7 @@ [quickbook 1.4] [authors [Cacciola Carballal, Fernando Luis]] [copyright 2003-2007 Fernando Luis Cacciola Carballal] - [copyright 2014-2024 Andrzej Krzemieński] + [copyright 2014-2026 Andrzej Krzemieński] [category miscellaneous] [id optional] [dirname optional] diff --git a/doc/01_quick_start.qbk b/doc/01_quick_start.qbk index 9d2c35d..0106e3c 100644 --- a/doc/01_quick_start.qbk +++ b/doc/01_quick_start.qbk @@ -124,7 +124,6 @@ Suppose we want to implement a ['lazy load] optimization. This is because we do `optional`'s default constructor creates an uninitialized optional. No call to `Resource`'s default constructor is attempted. `Resource` doesn't have to be __STD_DEFAULT_CONSTRUCTIBLE__. In function `getResource` we first check if `resource_` is initialized. This time we do not use the contextual conversion to `bool`, but a comparison with `boost::none`. These two ways are equivalent. Function `emplace` initializes the optional in-place by perfect-forwarding the arguments to the constructor of `Resource`. No copy- or move-construction is involved here. `Resource` doesn't even have to be `MoveConstructible`. -[note Function `emplace` is only available on compilers that support rvalue references and variadic templates. If your compiler does not support these features and you still need to avoid any move-constructions, use [link boost_optional.design.in_place_factories In-Place Factories].] [endsect] diff --git a/doc/17_in_place_factories.qbk b/doc/17_in_place_factories.qbk index d9df24d..fee5340 100644 --- a/doc/17_in_place_factories.qbk +++ b/doc/17_in_place_factories.qbk @@ -136,4 +136,6 @@ In-place factories can be used generically by the wrapper and user as follows: The factories are implemented in the headers: __IN_PLACE_FACTORY_HPP__ and __TYPED_IN_PLACE_FACTORY_HPP__ +[caution The support for in-place factories is deprecated. Use constructor taking `in_place_init` tag and function `.emplace()` instead.] + [endsect] diff --git a/doc/27_ref_optional_synopsis.qbk b/doc/27_ref_optional_synopsis.qbk index d0df811..9971f1b 100644 --- a/doc/27_ref_optional_synopsis.qbk +++ b/doc/27_ref_optional_synopsis.qbk @@ -95,10 +95,10 @@ namespace boost { class in_place_init_t { /* see below */ } ; - const in_place_init_t in_place_init ( /* see below */ ) ; + inline constexpr in_place_init_t in_place_init ( /* see below */ ) ; class in_place_init_if_t { /*see below*/ } ; - const in_place_init_if_t in_place_init_if ( /*see below*/ ) ; + inline constexpr in_place_init_if_t in_place_init_if ( /*see below*/ ) ; } @@ -123,33 +123,33 @@ They are empty, trivially copyable classes with disabled default constructor. typedef T * pointer_type ; typedef T const* pointer_const_type ; - optional () noexcept ; ``[link reference_optional_constructor __GO_TO__]`` + constexpr optional () noexcept ; ``[link reference_optional_constructor __GO_TO__]`` - optional ( none_t ) noexcept ; ``[link reference_optional_constructor_none_t __GO_TO__]`` + constexpr optional ( none_t ) noexcept ; ``[link reference_optional_constructor_none_t __GO_TO__]`` - optional ( T const& v ) ; ``[link reference_optional_constructor_value __GO_TO__]`` + constexpr optional ( T const& v ) ; ``[link reference_optional_constructor_value __GO_TO__]`` - optional ( T&& v ) ; ``[link reference_optional_constructor_move_value __GO_TO__]`` + constexpr optional ( T&& v ) ; ``[link reference_optional_constructor_move_value __GO_TO__]`` - optional ( bool condition, T const& v ) ; ``[link reference_optional_constructor_bool_value __GO_TO__]`` + constexpr optional ( bool condition, T const& v ) ; ``[link reference_optional_constructor_bool_value __GO_TO__]`` - optional ( optional const& rhs ) ; ``[link reference_optional_constructor_optional __GO_TO__]`` + constexpr optional ( optional const& rhs ) ; ``[link reference_optional_constructor_optional __GO_TO__]`` - optional ( optional&& rhs ) noexcept(``['see below]``) ; ``[link reference_optional_move_constructor_optional __GO_TO__]`` + constexpr optional ( optional&& rhs ) noexcept(``['see below]``) ; ``[link reference_optional_move_constructor_optional __GO_TO__]`` - template explicit optional ( optional const& rhs ) ; ``[link reference_optional_constructor_other_optional __GO_TO__]`` + template constexpr explicit optional ( optional const& rhs ) ; ``[link reference_optional_constructor_other_optional __GO_TO__]`` - template explicit optional ( optional&& rhs ) ; ``[link reference_optional_move_constructor_other_optional __GO_TO__]`` + template constexpr explicit optional ( optional&& rhs ) ; ``[link reference_optional_move_constructor_other_optional __GO_TO__]`` - template explicit optional ( in_place_init_t, Args&&... args ) ; ``[link reference_optional_in_place_init __GO_TO__]`` + template constexpr explicit optional ( in_place_init_t, Args&&... args ) ; ``[link reference_optional_in_place_init __GO_TO__]`` - template explicit optional ( in_place_init_if_t, bool condition, Args&&... args ) ; ``[link reference_optional_in_place_init_if __GO_TO__]`` + template constexpr explicit optional ( in_place_init_if_t, bool condition, Args&&... args ) ; ``[link reference_optional_in_place_init_if __GO_TO__]`` template explicit optional ( InPlaceFactory const& f ) ; ``[link reference_optional_constructor_factory __GO_TO__]`` template explicit optional ( TypedInPlaceFactory const& f ) ; ``[link reference_optional_constructor_factory __GO_TO__]`` - optional& operator = ( none_t ) noexcept ; ``[link reference_optional_operator_equal_none_t __GO_TO__]`` + constexpr optional& operator = ( none_t ) noexcept ; ``[link reference_optional_operator_equal_none_t __GO_TO__]`` optional& operator = ( T const& v ) ; ``[link reference_optional_operator_equal_value __GO_TO__]`` @@ -169,44 +169,42 @@ They are empty, trivially copyable classes with disabled default constructor. template optional& operator = ( TypedInPlaceFactory const& f ) ; ``[link reference_optional_operator_equal_factory __GO_TO__]`` - T const& get() const ; ``[link reference_optional_get __GO_TO__]`` - T& get() ; ``[link reference_optional_get __GO_TO__]`` + constexpr T const& get() const ; ``[link reference_optional_get __GO_TO__]`` + constexpr T& get() ; ``[link reference_optional_get __GO_TO__]`` - T const* operator ->() const ; ``[link reference_optional_operator_arrow __GO_TO__]`` - T* operator ->() ; ``[link reference_optional_operator_arrow __GO_TO__]`` + constexpr T const* operator ->() const ; ``[link reference_optional_operator_arrow __GO_TO__]`` + constexpr T* operator ->() ; ``[link reference_optional_operator_arrow __GO_TO__]`` - T const& operator *() const& ; ``[link reference_optional_operator_asterisk __GO_TO__]`` - T& operator *() & ; ``[link reference_optional_operator_asterisk __GO_TO__]`` - T&& operator *() && ; ``[link reference_optional_operator_asterisk_move __GO_TO__]`` + constexpr T const& operator *() const& ; ``[link reference_optional_operator_asterisk __GO_TO__]`` + constexpr T& operator *() & ; ``[link reference_optional_operator_asterisk __GO_TO__]`` + constexpr T&& operator *() && ; ``[link reference_optional_operator_asterisk_move __GO_TO__]`` - T const& value() const& ; ``[link reference_optional_value __GO_TO__]`` - T& value() & ; ``[link reference_optional_value __GO_TO__]`` - T&& value() && ; ``[link reference_optional_value_move __GO_TO__]`` + constexpr T const& value() const& ; ``[link reference_optional_value __GO_TO__]`` + constexpr T& value() & ; ``[link reference_optional_value __GO_TO__]`` + constexpr T&& value() && ; ``[link reference_optional_value_move __GO_TO__]`` - template T value_or( U && v ) const& ; ``[link reference_optional_value_or __GO_TO__]`` - template T value_or( U && v ) && ; ``[link reference_optional_value_or_move __GO_TO__]`` + template constexpr T value_or( U && v ) const& ; ``[link reference_optional_value_or __GO_TO__]`` + template constexpr T value_or( U && v ) && ; ``[link reference_optional_value_or_move __GO_TO__]`` - template T value_or_eval( F f ) const& ; ``[link reference_optional_value_or_call __GO_TO__]`` - template T value_or_eval( F f ) && ; ``[link reference_optional_value_or_call_move __GO_TO__]`` + template constexpr T value_or_eval( F f ) const& ; ``[link reference_optional_value_or_call __GO_TO__]`` + template constexpr T value_or_eval( F f ) && ; ``[link reference_optional_value_or_call_move __GO_TO__]`` - template auto map( F f ) const& -> ``['see below]``; ``[link reference_optional_map __GO_TO__]`` - template auto map( F f ) & -> ``['see below]``; ``[link reference_optional_map __GO_TO__]`` - template auto map( F f ) && -> ``['see below]``; ``[link reference_optional_map_move __GO_TO__]`` + template constexpr auto map( F f ) const& -> ``['see below]``; ``[link reference_optional_map __GO_TO__]`` + template constexpr auto map( F f ) & -> ``['see below]``; ``[link reference_optional_map __GO_TO__]`` + template constexpr auto map( F f ) && -> ``['see below]``; ``[link reference_optional_map_move __GO_TO__]`` - template auto flat_map( F f ) const& -> ``['see below]``; ``[link reference_optional_flat_map __GO_TO__]`` - template auto flat_map( F f ) & -> ``['see below]``; ``[link reference_optional_flat_map __GO_TO__]`` - template auto flat_map( F f ) && -> ``['see below]``; ``[link reference_optional_flat_map_move __GO_TO__]`` + template constexpr auto flat_map( F f ) const& -> ``['see below]``; ``[link reference_optional_flat_map __GO_TO__]`` + template constexpr auto flat_map( F f ) & -> ``['see below]``; ``[link reference_optional_flat_map __GO_TO__]`` + template constexpr auto flat_map( F f ) && -> ``['see below]``; ``[link reference_optional_flat_map_move __GO_TO__]`` T const* get_ptr() const ; ``[link reference_optional_get_ptr __GO_TO__]`` T* get_ptr() ; ``[link reference_optional_get_ptr __GO_TO__]`` - bool has_value() const noexcept ; ``[link reference_optional_operator_bool __GO_TO__]`` + constexpr bool has_value() const noexcept ; ``[link reference_optional_operator_bool __GO_TO__]`` - explicit operator bool() const noexcept ; ``[link reference_optional_operator_bool __GO_TO__]`` + constexpr explicit operator bool() const noexcept ; ``[link reference_optional_operator_bool __GO_TO__]`` - bool operator!() const noexcept ; ``[link reference_optional_operator_not __GO_TO__]`` - - void reset() noexcept ; ``[link reference_optional_reset __GO_TO__]`` + constexpr void reset() noexcept ; ``[link reference_optional_reset __GO_TO__]`` // deprecated methods @@ -214,7 +212,7 @@ They are empty, trivially copyable classes with disabled default constructor. void reset ( T const& ) ; ``[link reference_optional_reset_value __GO_TO__]`` // (deprecated) - bool is_initialized() const ; ``[link reference_optional_is_initialized __GO_TO__]`` + constexpr bool is_initialized() const ; ``[link reference_optional_is_initialized __GO_TO__]`` // (deprecated) T const& get_value_or( T const& default ) const ; ``[link reference_optional_get_value_or_value __GO_TO__]`` @@ -239,9 +237,9 @@ They are empty, trivially copyable classes with disabled default constructor. typedef T* pointer_type; typedef T* pointer_const_type; // no const propagation - optional () noexcept ; ``[link reference_optional_ref_default_ctor __GO_TO__]`` + constexpr optional () noexcept ; ``[link reference_optional_ref_default_ctor __GO_TO__]`` - optional ( none_t ) noexcept ; ``[link reference_optional_ref_default_ctor __GO_TO__]`` + constexpr optional ( none_t ) noexcept ; ``[link reference_optional_ref_default_ctor __GO_TO__]`` template optional(R&& r) noexcept ; ``[link reference_optional_ref_value_ctor __GO_TO__]`` diff --git a/doc/28_ref_optional_semantics.qbk b/doc/28_ref_optional_semantics.qbk index 9a5986f..8447d06 100644 --- a/doc/28_ref_optional_semantics.qbk +++ b/doc/28_ref_optional_semantics.qbk @@ -23,7 +23,7 @@ __SPACE__ [#reference_optional_constructor] -[: `optional::optional() noexcept;`] +[: `constexpr optional::optional() noexcept;`] * [*Effect:] Default-Constructs an `optional`. * [*Postconditions:] `*this` is [_uninitialized]. @@ -38,7 +38,7 @@ __SPACE__ [#reference_optional_constructor_none_t] -[: `optional::optional( none_t ) noexcept;`] +[: `constexpr optional::optional( none_t ) noexcept;`] * [*Effect:] Constructs an `optional` uninitialized. * [*Postconditions:] `*this` is [_uninitialized]. @@ -56,7 +56,7 @@ __SPACE__ [#reference_optional_constructor_value] -[: `optional::optional( T const& v )`] +[: `constexpr optional::optional( T const& v )`] * [*Requires:] `is_copy_constructible::value` is `true`. * [*Effect:] Directly-Constructs an `optional`. @@ -78,7 +78,7 @@ __SPACE__ [#reference_optional_constructor_move_value] -[: `optional::optional( T&& v )`] +[: `constexpr optional::optional( T&& v )`] * [*Requires:] `is_move_constructible::value` is `true`. * [*Effect:] Directly-Move-Constructs an `optional`. @@ -99,7 +99,7 @@ __SPACE__ [#reference_optional_constructor_bool_value] -[: `optional::optional( bool condition, T const& v ) ;` ] +[: `constexpr optional::optional( bool condition, T const& v ) ;` ] * If condition is true, same as: @@ -114,7 +114,7 @@ __SPACE__ [#reference_optional_constructor_optional] -[: `optional::optional( optional const& rhs );`] +[: `constexpr optional::optional( optional const& rhs );`] * [*Requires:] `is_copy_constructible::value` is `true`. * [*Effect:] Copy-Constructs an `optional`. @@ -144,7 +144,7 @@ __SPACE__ [#reference_optional_move_constructor_optional] -[: `optional::optional( optional&& rhs ) noexcept(`['see below]`);`] +[: `constexpr optional::optional( optional&& rhs ) noexcept(`['see below]`);`] * [*Requires:] `is_move_constructible::value` is `true`. * [*Effect:] Move-constructs an `optional`. @@ -178,7 +178,7 @@ __SPACE__ [#reference_optional_constructor_other_optional] -[: `template explicit optional::optional( optional const& rhs );`] +[: `template constexpr explicit optional::optional( optional const& rhs );`] * [*Effect:] Copy-Constructs an `optional`. * [*Postconditions:] If `rhs` is initialized, `*this` is initialized and its @@ -202,7 +202,7 @@ __SPACE__ [#reference_optional_move_constructor_other_optional] -[: `template explicit optional::optional( optional&& rhs );`] +[: `template constexpr explicit optional::optional( optional&& rhs );`] * [*Effect:] Move-constructs an `optional`. * [*Postconditions:] If `rhs` is initialized, `*this` is initialized and its @@ -226,7 +226,7 @@ __SPACE__ [#reference_optional_in_place_init] -[: `template explicit optional::optional( in_place_init_t, Args&&... ars );`] +[: `template constexpr explicit optional::optional( in_place_init_t, Args&&... ars );`] * [*Requires:] `is_constructible_v` is `true`. * [*Effect:] Initializes the contained value as if direct-non-list-initializing an object of type `T` with the @@ -251,7 +251,7 @@ __SPACE__ [#reference_optional_in_place_init_if] -[: `template explicit optional::optional( in_place_init_if_t, bool condition, Args&&... ars );`] +[: `template constexpr explicit optional::optional( in_place_init_if_t, bool condition, Args&&... ars );`] * [*Requires:] `is_constructible_v` is `true`. * [*Effect:] If `condition` is `true`, initializes the contained value as if direct-non-list-initializing an object of type `T` with the arguments `std::forward(args)...`. @@ -302,7 +302,7 @@ __SPACE__ [#reference_optional_operator_equal_none_t] -[: `optional& optional::operator= ( none_t ) noexcept;`] +[: `constexpr optional& optional::operator= ( none_t ) noexcept;`] * [*Effect:] If `*this` is initialized destroys its contained value. * [*Postconditions: ] `*this` is uninitialized. @@ -502,7 +502,7 @@ __SPACE__ * [*Postconditions: ] `*this` is [_initialized]. * [*Throws:] Whatever the selected `T`'s constructor throws. * [*Exception Safety:] If an exception is thrown during the initialization of `T`, `*this` is ['uninitialized]. -* [*Notes:] `T` need not be __MOVE_CONSTRUCTIBLE__ or `MoveAssignable`. +* [*Notes:] `T` need not be __MOVE_CONSTRUCTIBLE__ or `MoveAssignable`. * [*Example:] `` T v; @@ -540,15 +540,15 @@ __SPACE__ [#reference_optional_reset] -[: `void optional::reset() noexcept ;`] +[: `constexpr void optional::reset() noexcept ;`] * [*Effects:] Same as `operator=( none_t );` __SPACE__ [#reference_optional_get] -[: `T const& optional::get() const ;`] -[: `T& optional::get() ;`] +[: `constexpr T const& optional::get() const ;`] +[: `constexpr T& optional::get() ;`] [: `inline T const& get ( optional const& ) ;`] [: `inline T& get ( optional &) ;`] @@ -563,8 +563,8 @@ __SPACE__ [#reference_optional_operator_asterisk] -[: `T const& optional::operator*() const& ;`] -[: `T& optional::operator*() &;`] +[: `constexpr T const& optional::operator*() const& ;`] +[: `constexpr T& optional::operator*() &;`] * [*Requires:] `*this` is initialized * [*Returns:] A reference to the contained value @@ -585,7 +585,7 @@ __SPACE__ [#reference_optional_operator_asterisk_move] -[: `T&& optional::operator*() &&;`] +[: `constexpr T&& optional::operator*() &&;`] * [*Requires:] `*this` contains a value. * [*Effects:] Equivalent to `return std::move(*val);`. @@ -596,8 +596,8 @@ __SPACE__ [#reference_optional_value] -[: `T const& optional::value() const& ;`] -[: `T& optional::value() & ;`] +[: `constexpr T const& optional::value() const& ;`] +[: `constexpr T& optional::value() & ;`] * [*Effects:] Equivalent to `return bool(*this) ? *val : throw bad_optional_access();`. * [*Notes:] On compilers that do not support ref-qualifiers on member functions these two overloads are replaced with the classical two: a `const` and non-`const` member functions. @@ -620,7 +620,7 @@ __SPACE__ [#reference_optional_value_move] -[: `T&& optional::value() && ;`] +[: `constexpr T&& optional::value() && ;`] * [*Effects:] Equivalent to `return bool(*this) ? std::move(*val) : throw bad_optional_access();`. * [*Notes:] On compilers that do not support ref-qualifiers on member functions this overload is not present. @@ -630,17 +630,25 @@ __SPACE__ [#reference_optional_value_or] -[: `template T optional::value_or(U && v) const& ;`] +[: `template constexpr T optional::value_or(U && v) const& ;`] * [*Effects:] Equivalent to `if (*this) return **this; else return std::forward(v);`. * [*Remarks:] If `T` is not __COPY_CONSTRUCTIBLE__ or `U &&` is not convertible to `T`, the program is ill-formed. * [*Notes:] On compilers that do not support ref-qualifiers on member functions this overload is replaced with the `const`-qualified member function. On compilers without rvalue reference support the type of `v` becomes `U const&`. +* [*Example:] +`` +optional oN, o1(1); + +assert (o1.value_or(9) == 1); +assert (oN.value_or(9) == 9); +assert (oN.value_or({}) == 0); +`` __SPACE__ [#reference_optional_value_or_move] -[: `template T optional::value_or(U && v) && ;`] +[: `template constexpr T optional::value_or(U && v) && ;`] * [*Effects:] Equivalent to `if (*this) return std::move(**this); else return std::forward(v);`. * [*Remarks:] If `T` is not __MOVE_CONSTRUCTIBLE__ or `U &&` is not convertible to `T`, the program is ill-formed. @@ -650,7 +658,7 @@ __SPACE__ [#reference_optional_value_or_call] -[: `template T optional::value_or_eval(F f) const& ;`] +[: `template constexpr T optional::value_or_eval(F f) const& ;`] * [*Requires:] `T` is __COPY_CONSTRUCTIBLE__ and `F` models a __SGI_GENERATOR__ whose result type is convertible to `T`. * [*Effects:] `if (*this) return **this; else return f();`. @@ -677,7 +685,7 @@ __SPACE__ [#reference_optional_value_or_call_move] -[: `template T optional::value_or_eval(F f) && ;`] +[: `template constexpr T optional::value_or_eval(F f) && ;`] * [*Requires:] `T` is __MOVE_CONSTRUCTIBLE__ and `F` models a __SGI_GENERATOR__ whose result type is convertible to `T`. * [*Effects:] `if (*this) return std::move(**this); else return f();`. @@ -687,8 +695,8 @@ __SPACE__ [#reference_optional_map] -[: `template auto optional::map(F f) const& -> `['see below]` ;`] -[: `template auto optional::map(F f) & -> `['see below]` ;`] +[: `template constexpr auto optional::map(F f) const& -> `['see below]` ;`] +[: `template constexpr auto optional::map(F f) & -> `['see below]` ;`] * [*Effects:] `if (*this) return f(**this); else return none;` * [*Notes:] The return type of these overloads is `optional`. On compilers that do not support ref-qualifiers on member functions, these two (as well as the next one) overloads are replaced with good old const and non-const overloads. @@ -706,7 +714,7 @@ __SPACE__ [#reference_optional_map_move] -[: `template auto optional::map(F f) && -> `['see below]` ;`] +[: `template constexpr auto optional::map(F f) && -> `['see below]` ;`] * [*Effects:] `if (*this) return f(std::move(**this)); else return none;` * [*Notes:] The return type of this overload is `optional`. @@ -715,8 +723,8 @@ __SPACE__ [#reference_optional_flat_map] -[: `template auto optional::flat_map(F f) const& -> `['see below]` ;`] -[: `template auto optional::flat_map(F f) & -> `['see below]` ;`] +[: `template constexpr auto optional::flat_map(F f) const& -> `['see below]` ;`] +[: `template constexpr auto optional::flat_map(F f) & -> `['see below]` ;`] * [*Requires:] The return type of expression `f(**this)` is `optional` for some object or reference type `U`. * [*Effects:] `if (*this) return f(**this); else return none;` @@ -736,7 +744,7 @@ __SPACE__ [#reference_optional_flat_map_move] -[: `template auto optional::flat_map(F f) && -> `['see below]` ;`] +[: `template constexpr auto optional::flat_map(F f) && -> `['see below]` ;`] * [*Requires:] The return type of expression `f(std::move(**this))` is `optional` for some object or reference type `U`. * [*Effects:] `if (*this) return f(std::move(**this)); else return none;` @@ -793,8 +801,8 @@ __SPACE__ [#reference_optional_operator_arrow] -[: `T const* optional::operator ->() const ;`] -[: `T* optional::operator ->() ;`] +[: `constexpr T const* optional::operator ->() const ;`] +[: `constexpr T* optional::operator ->() ;`] * [*Requires: ] `*this` is initialized. * [*Returns:] A pointer to the contained value. @@ -812,44 +820,29 @@ __SPACE__ [#reference_optional_operator_bool] -[: `explicit optional::operator bool() const noexcept ;`] -[: `bool optional::has_value() const noexcept ;`] +[: `constexpr explicit optional::operator bool() const noexcept ;`] +[: `constexpr bool optional::has_value() const noexcept ;`] * [*Returns:] `get_ptr() != 0`. * [*Notes:] On compilers that do not support explicit conversion operators this falls back to safe-bool idiom. * [*Example:] `` -optional def ; -assert ( def == 0 ); -optional opt ( v ) ; -assert ( opt ); -assert ( opt != 0 ); +optional oN; +assert (!oN); +assert (!oN.has_value()); + +optional o1(1); +assert (o1); +assert (o1.has_value()); +assert (!!o1); // the "double-bang" idiom `` __SPACE__ -[#reference_optional_operator_not] - -[: `bool optional::operator!() noexcept ;`] - -* [*Returns:] If `*this` is uninitialized, `true`; else `false`. -* [*Notes:] This operator is provided for those compilers which can't -use the ['unspecified-bool-type operator] in certain boolean contexts. -* [*Example:] -`` -optional opt ; -assert ( !opt ); -*opt = some_T ; - -// Notice the "double-bang" idiom here. -assert ( !!opt ) ; -`` - -__SPACE__ [#reference_optional_is_initialized] -[: `bool optional::is_initialized() const ;`] +[: `constexpr bool optional::is_initialized() const ;`] * [*Deprecated:] Same as `explicit operator bool () ;` @@ -862,8 +855,8 @@ __SPACE__ [#reference_optional_ref_default_ctor] -[: `optional::optional() noexcept;`] -[: `optional::optional(none_t) noexcept;`] +[: `constexpr optional::optional() noexcept;`] +[: `constexpr optional::optional(none_t) noexcept;`] * [*Postconditions:] `bool(*this) == false`; `*this` refers to nothing. diff --git a/doc/90_dependencies.qbk b/doc/90_dependencies.qbk index 8f849a1..6409651 100644 --- a/doc/90_dependencies.qbk +++ b/doc/90_dependencies.qbk @@ -9,7 +9,20 @@ ] -[section Dependencies and Portability] +[section Dependencies and Portability][#minimum_system_requirements] + + +[section Minimum System Requirements] +This library requires C++11 as minimum. However some features are disabled. + +For C++14 and higher this library provides a `constexpr` interface for non-mutable +member functions and some mutable member functions. + +[note For types that overload unary `operator&` (address) some member functions in `optional`, like `operator->`, +cannot be implemented as `constexpr` on some compilers.] + +[endsect] + [section Dependencies] The implementation uses the following other Boost modules: diff --git a/doc/92_relnotes.qbk b/doc/92_relnotes.qbk index 7d0df4c..7217f87 100644 --- a/doc/92_relnotes.qbk +++ b/doc/92_relnotes.qbk @@ -1,7 +1,7 @@ [/ Boost.Optional - Copyright (c) 2015 - 2023 Andrzej Krzemienski + Copyright (c) 2015 - 2026 Andrzej Krzemienski Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -11,6 +11,19 @@ [section:relnotes Release Notes] +[heading Boost Release 1.91] + +* All non-mutating and some mutating functions become `constexpr` in C++14 compilers. + This addresses [@https://github.com/boostorg/optional/issues/143 issue #143]. +* For C++14 applied a couple of small changes to get closer to the interface of `std::optional`: + * Enabled syntax `o = {}` for putting optional objects to no-value state. + * Enabled syntax `o.value_or({})`, which uses a default-constructed `T`. + * Construct `o = u`, where `o` is of type `optional` and `u` is of type `U` convertible to `T`, does not create a temporary `T`. +* *Breaking change.* For C++14 compilers abandoned the mechanism for customizing + `swap`. Hardly anyone knows about this mechanism and it was never documented. + + + [heading Boost Release 1.87] * *Breaking change.* Dropped support for C++03. C++11 is now the required minimum; at least some C++11 features. @@ -22,7 +35,7 @@ * *Warning.* In the future releases we intend to introduce the range interface into `optional`, so that `std::ranges::range>` will be `true`. This may affect the overload resolution in programs that make decisions based - on predicates such as `std::ranges::range`. + on predicates such as `std::ranges::range`. * Tags `in_place_init` and `in_place_init_if` become `inline constexpr` and therewith leave smaller footprint in the executable. This addresses [@https://github.com/boostorg/optional/issues/103 issue #103]. diff --git a/include/boost/optional/detail/constexpr_optional.hpp b/include/boost/optional/detail/constexpr_optional.hpp new file mode 100644 index 0000000..ecf8a88 --- /dev/null +++ b/include/boost/optional/detail/constexpr_optional.hpp @@ -0,0 +1,571 @@ +// Copyright (C) 2026 Andrzej Krzemieński. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com +// +// +// This header provides definitions required by any specialization of +// optional<>. + +#ifndef BOOST_OPTIONAL_DETAIL_CONSTEXPR_OPTIONAL_01FEB2026_HPP +#define BOOST_OPTIONAL_DETAIL_CONSTEXPR_OPTIONAL_01FEB2026_HPP + +#include +#include +#include +#include +#include +#include + + + +// This macro shall be put in the position of a template parameter. +// It emulates the requires clause. +# define BOOST_OPTIONAL_REQUIRES(...) typename ::std::enable_if<__VA_ARGS__::value, bool>::type = false + + +// In C++20 we have `std::construct_at()` which is a constexpr equivalent of +// placement-new. We can then make more functions constexpr. +// TBD: This additional constexpr-ication is left for the future. +# define BOOST_OPTIONAL_CXX20_CONSTEXPR + + +// Missing C++17 type traits +namespace boost { namespace optional_detail { + +template +struct conjunction : ::std::true_type {}; + +template +struct conjunction : B1 {}; + +template +struct conjunction + : ::std::conditional_t, B1> {}; + +}} + +namespace boost { namespace optional_detail { + +// Tag to indicate a special-purpose constructor +BOOST_INLINE_VARIABLE constexpr struct trivial_init_t{} trivial_init{}; + + +template +union constexpr_union_storage_t +{ + static_assert(::std::is_trivially_destructible::value, "!!"); + + unsigned char dummy_; + T value_; + + constexpr constexpr_union_storage_t( trivial_init_t ) noexcept : dummy_() {}; + + template + constexpr constexpr_union_storage_t( Args&&... args ) : value_(forward_(args)...) {} + + //~constexpr_union_storage_t() = default; // No need to destroy a trivially-destructible type +}; + +template +union fallback_union_storage_t +{ + unsigned char dummy_; + T value_; + + constexpr fallback_union_storage_t( trivial_init_t ) noexcept : dummy_() {}; + + template + constexpr fallback_union_storage_t( Args&&... args ) : value_(forward_(args)...) {} + + ~fallback_union_storage_t(){} // My owner will destroy the `T` if needed. + // Cannot default in a union with nontrivial `T`. +}; + + +// `guarded_storage` is a union + a flag indicating if a `T` has been initialized. +// this way the destructor knows if it should destroy the `T`. +template +struct constexpr_guarded_storage +{ + static_assert(::std::is_trivially_destructible::value, "!!"); + + bool init_; + constexpr_union_storage_t storage_; + + constexpr constexpr_guarded_storage() noexcept : init_(false), storage_(trivial_init) {}; + + explicit constexpr constexpr_guarded_storage(const T& v) : init_(true), storage_(v) {} + + explicit constexpr constexpr_guarded_storage(T&& v) : init_(true), storage_(move_(v)) {} + + template explicit constexpr constexpr_guarded_storage(optional_ns::in_place_init_t, Args&&... args) + : init_(true), storage_(forward_(args)...) {} + + template >)> + constexpr explicit constexpr_guarded_storage(optional_ns::in_place_init_t, ::std::initializer_list il, Args&&... args) + : init_(true), storage_(il, forward_(args)...) {} + + ~constexpr_guarded_storage() = default; +}; + + +template +struct fallback_guarded_storage +{ + bool init_; + fallback_union_storage_t storage_; + + constexpr fallback_guarded_storage() noexcept : init_(false), storage_(trivial_init) {}; + + explicit constexpr fallback_guarded_storage(const T& v) : init_(true), storage_(v) {} + + explicit constexpr fallback_guarded_storage(T&& v) : init_(true), storage_(move_(v)) {} + + template explicit fallback_guarded_storage(optional_ns::in_place_init_t, Args&&... args) + : init_(true), storage_(forward_(args)...) {} + + template >)> + explicit fallback_guarded_storage(optional_ns::in_place_init_t, ::std::initializer_list il, Args&&... args) + : init_(true), storage_(il, forward_(args)...) {} + + ~fallback_guarded_storage() { if (init_) storage_.value_.T::~T(); } +}; + + +template +using guarded_storage = typename ::std::conditional< + ::std::is_trivially_destructible::value, // if possible + constexpr_guarded_storage::type>, // use storage with trivial destructor + fallback_guarded_storage::type> +>::type; + + +}} + + +namespace boost { + + template + class optional : public optional_detail::optional_tag + { + using storage_t = optional_detail::guarded_storage; + storage_t storage; + static_assert( !::std::is_same::type, none_t>::value, "bad T" ); + static_assert( !::std::is_same::type, in_place_init_t>::value, "bad T" ); + + + constexpr typename ::std::remove_const::type* dataptr() { return ::boost::addressof(storage.storage_.value_); } + constexpr const T* dataptr() const { return ::boost::addressof(storage.storage_.value_); } + + constexpr const T& contained_val() const& { return storage.storage_.value_; } + constexpr T&& contained_val() && { return optional_detail::move_(storage.storage_.value_); } + constexpr T& contained_val() & { return storage.storage_.value_; } + + template + BOOST_OPTIONAL_CXX20_CONSTEXPR void initialize(Args&&... args) + { + BOOST_ASSERT(!storage.init_); + ::new (static_cast(dataptr())) T(optional_detail::forward_(args)...); + storage.init_ = true; + } + + public: + using value_type = T; + using unqualified_value_type = typename ::std::remove_const::type; + + using reference_type = T&; + using reference_const_type = T const&; + using argument_type = T const&; + using rval_reference_type = T&&; + using reference_type_of_temporary_wrapper = T&&; + using pointer_type = T*; + using pointer_const_type = T const*; + + + constexpr bool is_initialized() const noexcept { return storage.init_; } + + constexpr optional() noexcept : storage() {}; + constexpr optional(none_t) noexcept : storage() {}; + + constexpr optional(const T& v) : storage(v) {} + constexpr optional(T&& v) : storage(optional_detail::move_(v)) {} + + constexpr optional(bool cond, const T& v) + : storage(cond ? storage_t(v) : storage_t()) + {} + + constexpr optional(bool cond, T&& v) + : storage(cond ? storage_t(optional_detail::move_(v)) : storage_t()) + {} + + constexpr optional(const optional& rhs) + : storage(rhs.is_initialized() ? storage_t(*rhs) : storage_t()) + {} + + constexpr optional(optional&& rhs) + noexcept(::std::is_nothrow_move_constructible::value) + : storage(rhs.is_initialized() ? storage_t(*optional_detail::move_(rhs)) : storage_t()) + {} + + template )> + constexpr explicit optional(optional const& rhs) + : storage(rhs.is_initialized() ? storage_t(*rhs) : storage_t()) + {} + + template )> + constexpr explicit optional(optional && rhs) + : storage(rhs.is_initialized() ? storage_t(*optional_detail::move_(rhs)) : storage_t()) + {} + + template )> + constexpr explicit optional (FT&& factory) + : storage() + { + factory.apply(this->dataptr()); + storage.init_ = true ; + } + + template )> + constexpr explicit optional (FT&& factory) + : storage() + { + factory.template apply(this->dataptr()); + storage.init_ = true; + } + + template )> + constexpr explicit optional(U&& v) + : storage(optional_ns::in_place_init, optional_detail::forward_(v)) + {} + + template + constexpr explicit optional( in_place_init_t, Args&&... args ) + : storage(in_place_init, optional_detail::forward_(args)...) + {} + + template + constexpr explicit optional( in_place_init_if_t, bool cond, Args&&... args ) + : storage( cond ? storage_t(in_place_init, optional_detail::forward_(args)...) : storage_t() ) + {} + + constexpr void reset() noexcept + { + if (is_initialized()) dataptr()->T::~T(); + storage.init_ = false; + } + + BOOST_OPTIONAL_CXX20_CONSTEXPR void reset(const T& v) + { + *this = v; + } + + template + BOOST_OPTIONAL_CXX20_CONSTEXPR void emplace(Args&&... args) + { + reset(); static_assert(noexcept(reset())); + // <-- now we are not containing a value + initialize(optional_detail::forward_(args)...); + } + + + constexpr optional& operator=(none_t) noexcept + { + reset(); + return *this; + } + + BOOST_OPTIONAL_CXX20_CONSTEXPR optional& operator=(const optional& rhs) + { + if (has_value()) + { + if (rhs.has_value()) + **this = *rhs; + else + reset(); + } + else + { + if (rhs.has_value()) + initialize(*rhs); + } + + return *this; + } + + BOOST_OPTIONAL_CXX20_CONSTEXPR optional& operator=(optional&& rhs) + noexcept(::std::is_nothrow_move_assignable::value && ::std::is_nothrow_move_constructible::value) + { + if (has_value()) + { + if (rhs.has_value()) + **this = *optional_detail::move_(rhs); + else + reset(); + } + else + { + if (rhs.has_value()) + initialize(*optional_detail::move_(rhs) ); + } + + return *this; + } + + template + BOOST_OPTIONAL_CXX20_CONSTEXPR optional& operator=(const optional& rhs) + { + if (has_value()) + { + if (rhs.has_value()) + **this = *rhs; + else + reset(); + } + else + { + if (rhs.has_value()) + initialize(*rhs); + } + + return *this; + } + + template + BOOST_OPTIONAL_CXX20_CONSTEXPR optional& operator=(optional&& rhs) + { + if (has_value()) + { + if (rhs.has_value()) + **this = *optional_detail::move_(rhs); + else + reset(); + } + else + { + if (rhs.has_value()) + initialize(*optional_detail::move_(rhs)); + } + + return *this; + } + + template ::type, + BOOST_OPTIONAL_REQUIRES(!::std::is_same::type, optional>), + BOOST_OPTIONAL_REQUIRES(!optional_detail::conjunction<::std::is_scalar, ::std::is_same>), + BOOST_OPTIONAL_REQUIRES(::std::is_constructible), + BOOST_OPTIONAL_REQUIRES(::std::is_assignable) + > + BOOST_OPTIONAL_CXX20_CONSTEXPR optional& operator=(U&& v) + { + if (is_initialized()) + contained_val() = optional_detail::forward_(v); + else + initialize(optional_detail::forward_(v)); + return *this; + } + + template )> + optional& operator=(F&& factory) + { + reset(); + factory.template apply(this->dataptr()); + storage.init_ = true; + return *this; + } + + template )> + optional& operator=(F&& factory) + { + reset(); + factory.apply(this->dataptr()); + storage.init_ = true; + return *this; + } + + BOOST_OPTIONAL_CXX20_CONSTEXPR + void swap(optional& rhs) + noexcept(::std::is_nothrow_move_constructible::value && noexcept(boost::core::invoke_swap(*rhs, *rhs))) + // I am cheating here. I need "swapabe" not "assignnable" trait. But this is best I can do in C++14 + { + if (is_initialized()) + { + if (rhs.is_initialized()) + boost::core::invoke_swap(contained_val(), rhs.contained_val()); + else + { rhs.initialize(optional_detail::move_(*this).contained_val()); reset(); } + } + else + { + if (rhs.is_initialized()) + { initialize(optional_detail::move_(rhs).contained_val()); rhs.reset(); } + } + } + + ~optional() = default; // The destructor in `storage`, based on the specialization + // will be trivial or not. + + constexpr bool has_value() const noexcept { return this->is_initialized(); } + constexpr explicit operator bool() const noexcept { return this->is_initialized(); } + + constexpr reference_const_type get() const { BOOST_ASSERT(this->is_initialized()) ; return this->contained_val(); } + constexpr reference_type get() { BOOST_ASSERT(this->is_initialized()) ; return this->contained_val(); } + + BOOST_DEPRECATED("use `value_or(v)` instead") + reference_const_type get_value_or (reference_const_type v) const { return this->is_initialized() ? this->contained_val() : v; } + + BOOST_DEPRECATED("use `value_or(v)` instead") + reference_type get_value_or (reference_type v) { return this->is_initialized() ? this->contained_val() : v; } + + pointer_const_type get_ptr() const { return is_initialized() ? dataptr() : nullptr; } + pointer_type get_ptr() { return is_initialized() ? dataptr() : nullptr; } + + constexpr reference_const_type operator*() const& { return this->get(); } + constexpr reference_type operator*() & { return this->get(); } + constexpr reference_type_of_temporary_wrapper operator*() && { return optional_detail::move_(this->get()); } + + constexpr pointer_const_type operator->() const { BOOST_ASSERT(this->is_initialized()) ; return this->dataptr(); } + constexpr pointer_type operator->() { BOOST_ASSERT(this->is_initialized()) ; return this->dataptr(); } + + constexpr reference_const_type value() const& + { + if (this->is_initialized()) + return this->get(); + else + boost::throw_exception(boost::bad_optional_access()); + } + + constexpr reference_type value() & + { + if (this->is_initialized()) + return this->get(); + else + boost::throw_exception(boost::bad_optional_access()); + } + + constexpr reference_type_of_temporary_wrapper value() && + { + if (this->is_initialized()) + return optional_detail::move_(this->get()); + else + boost::throw_exception(boost::bad_optional_access()); + } + + template + constexpr value_type value_or(U&& v) const& + { + if (this->is_initialized()) + return get(); + else + return optional_detail::forward_(v); + } + + template + constexpr value_type value_or(U&& v) && + { + if (this->is_initialized()) + return optional_detail::move_(get()); + else + return optional_detail::forward_(v); + } + + template + constexpr value_type value_or_eval(F f) const& + { + if (this->is_initialized()) + return get(); + else + return f(); + } + + template + constexpr value_type value_or_eval ( F f ) && + { + if (this->is_initialized()) + return optional_detail::move_(get()); + else + return f(); + } + + template + constexpr optional::type> map(F f) & + { + if (this->has_value()) + return f(get()); + else + return none; + } + + template + constexpr optional::type> map(F f) const& + { + if (this->has_value()) + return f(get()); + else + return none; + } + + template + constexpr optional::type> map(F f) && + { + if (this->has_value()) + return f(optional_detail::move_(this->get())); + else + return none; + } + + template + constexpr optional::type> + flat_map(F f) & + { + if (this->has_value()) + return f(get()); + else + return none; + } + + template + constexpr optional::type> + flat_map(F f) const& + { + if (this->has_value()) + return f(get()); + else + return none; + } + + template + constexpr optional::type> + flat_map(F f) && + { + if (this->has_value()) + return f(optional_detail::move_(get())); + else + return none; + } + + }; + + template + BOOST_OPTIONAL_CXX20_CONSTEXPR + void swap(optional& lhs, optional& rhs) + noexcept(::std::is_nothrow_move_constructible::value && noexcept(boost::core::invoke_swap(*lhs, *rhs))) + { + lhs.swap(rhs); + } + +} + + +// https://godbolt.org/z/ov1ahMsv6 + +#endif // BOOST_OPTIONAL_DETAIL_CONSTEXPR_OPTIONAL_01FEB2026_HPP diff --git a/include/boost/optional/detail/optional_common_defs.hpp b/include/boost/optional/detail/optional_common_defs.hpp new file mode 100644 index 0000000..2e92dee --- /dev/null +++ b/include/boost/optional/detail/optional_common_defs.hpp @@ -0,0 +1,180 @@ +// Copyright (C) 2024 Ryan Malcolm Underwood. +// Copyright (C) 2026 Andrzej Krzemieński. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/optional for documentation. +// +// You are welcome to contact the authors at: +// akrzemi1@gmail.com +// typenametea@gmail.com +// +// +// This header provides definitions required by any specialization of +// optional<>. + +#ifndef BOOST_OPTIONAL_DETAIL_OPTIONAL_COMMON_DEFS_01FEB2026_HPP +#define BOOST_OPTIONAL_DETAIL_OPTIONAL_COMMON_DEFS_01FEB2026_HPP + + +#include +#include +#include + +#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#include +#include +#endif + + +#ifndef BOOST_NO_CXX14_CONSTEXPR +# define BOOST_OPTIONAL_DECAY(T) typename ::std::decay::type +# define BOOST_OPTIONAL_IS_TAGGED(TAG, U) ::std::is_base_of +#else +# define BOOST_OPTIONAL_DECAY(T) BOOST_DEDUCED_TYPENAME boost::decay::type +# define BOOST_OPTIONAL_IS_TAGGED(TAG, U) boost::is_base_of +#endif + +namespace boost { + +template class optional; + + +// Boost-wide tags for recognizing "factories": a C++03 workaround +// for perfect forwarding. +class in_place_factory_base; +class typed_in_place_factory_base; + +} // namespace boost + + +// Traits for recognizing in-place factories +namespace boost { namespace optional_detail { + +template +struct is_in_place_factory : BOOST_OPTIONAL_IS_TAGGED(boost::in_place_factory_base, U) {}; + +template +struct is_typed_in_place_factory : BOOST_OPTIONAL_IS_TAGGED(boost::typed_in_place_factory_base, U) {}; + +}} + + + +/** This is a set of declarations that repeat those from the Standard Library + header but without having to drag its entire content. They also + add missing capabilities, like constexpr, in older compiler versions. + */ +namespace boost { namespace optional_detail { + +template +T declval_(); + +template +inline constexpr T&& forward_(typename ::std::remove_reference::type& t) noexcept +{ + return static_cast(t); +} + +template +inline constexpr T&& forward_(typename ::std::remove_reference::type&& t) noexcept +{ + static_assert(!::std::is_lvalue_reference::value, "Can not forward an rvalue as an lvalue."); + return static_cast(t); +} + +template +inline constexpr typename ::std::remove_reference::type&& move_(T&& t) noexcept +{ + return static_cast::type&&>(t); +} + +}} // namespace boost::optional_detail + + +/** This is a set of declarations that are not part of this library's interface. + They are implementation details. + */ +namespace boost { namespace optional_detail { + +/** This struct is used for tagging types that want to be recognized as + `optional`. If your class inherits directly or indirectly from `optional_tag` + the type traits and overloads will treat it as `optional<>`. + */ +struct optional_tag {}; + +/** `optional_value_type`: given type `X`: + * if `X` is an instance of `boost::optional`, returns its value_type, + * otherwise we get a SFINAE-able error. + */ +template +struct optional_value_type +{ +}; + +template +struct optional_value_type< ::boost::optional > +{ + typedef U type; +}; + + +/** This is an approximation of a 1-argument C++17 std::invoke_result. + */ +template ()(declval_()))> +struct result_of +{ + typedef Rslt type; +}; + +/** This type trait returns the following given the expression `f(ref)`: + * if the result is a specialization of `boost::optional`: its value_type, + * otherwise a SFINAE-able error. + */ +template ::type>::type> +struct result_value_type +{ + typedef Rslt type; +}; + +}} // namespace boost::optional_detail + + +/** The following two tags are intended to be used by library users. + The additional namespace is used in order to prevent the ADL from + dragging all functions from namespace `boost` in any unqualified name lookup + when these tags are involved. + */ +namespace boost { + +namespace optional_ns { + +/// a tag for in-place initialization of contained value +struct in_place_init_t +{ + struct init_tag{}; + BOOST_CONSTEXPR explicit in_place_init_t(init_tag){} +}; +BOOST_INLINE_CONSTEXPR in_place_init_t in_place_init ((in_place_init_t::init_tag())); + +/// a tag for conditional in-place initialization of contained value +struct in_place_init_if_t +{ + struct init_tag{}; + BOOST_CONSTEXPR explicit in_place_init_if_t(init_tag){} +}; +BOOST_INLINE_CONSTEXPR in_place_init_if_t in_place_init_if ((in_place_init_if_t::init_tag())); + +} // namespace optional_ns + +using optional_ns::in_place_init_t; +using optional_ns::in_place_init; +using optional_ns::in_place_init_if_t; +using optional_ns::in_place_init_if; + +} // namespace boost + + +#endif // BOOST_OPTIONAL_DETAIL_OPTIONAL_COMMON_DEFS_01FEB2026_HPP diff --git a/include/boost/optional/detail/optional_factory_support.hpp b/include/boost/optional/detail/optional_factory_support.hpp index efff92a..6014aaa 100644 --- a/include/boost/optional/detail/optional_factory_support.hpp +++ b/include/boost/optional/detail/optional_factory_support.hpp @@ -27,10 +27,4 @@ namespace boost_optional_detail } } -namespace boost -{ - class in_place_factory_base ; - class typed_in_place_factory_base ; -} - #endif // header guard diff --git a/include/boost/optional/detail/optional_hash.hpp b/include/boost/optional/detail/optional_hash.hpp index 234ee7b..b1605df 100644 --- a/include/boost/optional/detail/optional_hash.hpp +++ b/include/boost/optional/detail/optional_hash.hpp @@ -12,7 +12,7 @@ #ifndef BOOST_OPTIONAL_DETAIL_OPTIONAL_HASH_AJK_20MAY2022_HPP #define BOOST_OPTIONAL_DETAIL_OPTIONAL_HASH_AJK_20MAY2022_HPP -#include +//#include #include #if !defined(BOOST_OPTIONAL_CONFIG_DO_NOT_SPECIALIZE_STD_HASH) && !defined(BOOST_NO_CXX11_HDR_FUNCTIONAL) diff --git a/include/boost/optional/detail/optional_nonmember_interface.hpp b/include/boost/optional/detail/optional_nonmember_interface.hpp new file mode 100644 index 0000000..c4305fd --- /dev/null +++ b/include/boost/optional/detail/optional_nonmember_interface.hpp @@ -0,0 +1,117 @@ +// Copyright (C) 2003, 2008 Fernando Luis Cacciola Carballal. +// Copyright (C) 2014 - 2026 Andrzej Krzemieński. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/optional for documentation. +// +// You are welcome to contact the authors at: +// fernando_cacciola@hotmail.com +// akrzemi1@gmail.com +// +// You can file a GitHub issue at: +// https://github.com/boostorg/optional/issues + + +// This header provides definitions rof nonmember functions that still constitute +// the interface for class optional<>. + +#ifndef BOOST_OPTIONAL_DETAIL_OPTIONAL_NONMEMBER_INTERFACE_01FEB2026_HPP +#define BOOST_OPTIONAL_DETAIL_OPTIONAL_NONMEMBER_INTERFACE_01FEB2026_HPP + +namespace boost { + + +template +inline BOOST_CXX14_CONSTEXPR +optional make_optional ( T && v ) +{ + return optional(optional_detail::forward_(v)); +} + +// Returns optional(cond,v) +template +inline BOOST_CXX14_CONSTEXPR +optional make_optional ( bool cond, T && v ) +{ + return optional(cond,optional_detail::forward_(v)); +} + + +// Returns a reference to the value if this is initialized, otherwise, the behaviour is UNDEFINED. +// No-throw +template +inline BOOST_CXX14_CONSTEXPR +BOOST_DEDUCED_TYPENAME optional::reference_const_type +get ( optional const& opt ) +{ + return opt.get() ; +} + +template +inline BOOST_CXX14_CONSTEXPR +BOOST_DEDUCED_TYPENAME optional::reference_type +get ( optional& opt ) +{ + return opt.get() ; +} + +// Returns a pointer to the value if this is initialized, otherwise, returns NULL. +// No-throw +template +inline BOOST_CXX14_CONSTEXPR +BOOST_DEDUCED_TYPENAME optional::pointer_const_type +get ( optional const* opt ) +{ + return opt->get_ptr() ; +} + +template +inline BOOST_CXX14_CONSTEXPR +BOOST_DEDUCED_TYPENAME optional::pointer_type +get ( optional* opt ) +{ + return opt->get_ptr() ; +} + +// Returns a reference to the value if this is initialized, otherwise, the behaviour is UNDEFINED. +// No-throw +template +inline BOOST_CXX14_CONSTEXPR +BOOST_DEDUCED_TYPENAME optional::reference_const_type +get_optional_value_or ( optional const& opt, BOOST_DEDUCED_TYPENAME optional::reference_const_type v ) +{ + return opt.get_value_or(v) ; +} + +template +inline BOOST_CXX14_CONSTEXPR +BOOST_DEDUCED_TYPENAME optional::reference_type +get_optional_value_or ( optional& opt, BOOST_DEDUCED_TYPENAME optional::reference_type v ) +{ + return opt.get_value_or(v) ; +} + +// Returns a pointer to the value if this is initialized, otherwise, returns NULL. +// No-throw +template +inline BOOST_CXX14_CONSTEXPR +BOOST_DEDUCED_TYPENAME optional::pointer_const_type +get_pointer ( optional const& opt ) +{ + return opt.get_ptr() ; +} + +template +inline BOOST_CXX14_CONSTEXPR +BOOST_DEDUCED_TYPENAME optional::pointer_type +get_pointer ( optional& opt ) +{ + return opt.get_ptr() ; +} + +} // namespace boost + +#endif // BOOST_OPTIONAL_DETAIL_OPTIONAL_NONMEMBER_INTERFACE_01FEB2026_HPP diff --git a/include/boost/optional/detail/optional_reference_spec.hpp b/include/boost/optional/detail/optional_reference_spec.hpp index d8405f8..f0956e9 100644 --- a/include/boost/optional/detail/optional_reference_spec.hpp +++ b/include/boost/optional/detail/optional_reference_spec.hpp @@ -13,9 +13,21 @@ #define BOOST_OPTIONAL_DETAIL_OPTIONAL_REFERENCE_SPEC_AJK_03OCT2015_HPP #ifdef BOOST_OPTIONAL_CONFIG_NO_PROPER_ASSIGN_FROM_CONST_INT +#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION #include #include #endif +#endif + +#ifdef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +# define BOOST_OPTIONAL_TT_PREFIX ::std +#else +# define BOOST_OPTIONAL_TT_PREFIX boost +# define BOOST_OPTIONAL_REQUIRES(...) BOOST_DEDUCED_TYPENAME boost::enable_if_c<__VA_ARGS__::value, bool>::type = false +#endif + +# define BOOST_OPTIONAL_TT_TYPE(...) BOOST_DEDUCED_TYPENAME BOOST_OPTIONAL_TT_PREFIX::__VA_ARGS__::type +# define BOOST_OPTIONAL_TT_PRED(...) BOOST_OPTIONAL_TT_PREFIX::__VA_ARGS__::value # if 1 @@ -29,17 +41,17 @@ template void prevent_binding_rvalue() { #ifndef BOOST_OPTIONAL_CONFIG_ALLOW_BINDING_TO_RVALUES - static_assert(boost::is_lvalue_reference::value, + static_assert(BOOST_OPTIONAL_TT_PRED(is_lvalue_reference), "binding rvalue references to optional lvalue references is disallowed"); #endif } template -BOOST_DEDUCED_TYPENAME boost::remove_reference::type& forward_reference(T&& r) +BOOST_OPTIONAL_TT_TYPE(remove_reference)& forward_reference(T&& r) { - static_assert(boost::is_lvalue_reference::value, + static_assert(BOOST_OPTIONAL_TT_PRED(is_lvalue_reference), "binding rvalue references to optional lvalue references is disallowed"); - return optional_detail::forward(r); + return optional_detail::forward_(r); } #endif // BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES @@ -48,14 +60,14 @@ BOOST_DEDUCED_TYPENAME boost::remove_reference::type& forward_reference(T&& r template struct is_const_integral { - static const bool value = boost::is_const::value && boost::is_integral::value; + static const bool value = BOOST_OPTIONAL_TT_PRED(is_const) && BOOST_OPTIONAL_TT_PRED(is_integral); }; template struct is_const_integral_bad_for_conversion { #if (!defined BOOST_OPTIONAL_CONFIG_ALLOW_BINDING_TO_RVALUES) && (defined BOOST_OPTIONAL_CONFIG_NO_PROPER_CONVERT_FROM_CONST_INT) - static const bool value = boost::is_const::value && boost::is_integral::value; + static const bool value = is_const_integral::value; #else static const bool value = false; #endif @@ -90,15 +102,15 @@ struct is_optional_< ::boost::optional > template struct is_no_optional { - static const bool value = !is_optional_::type>::value; + static const bool value = !is_optional_::value; }; template struct is_same_decayed { - static const bool value = ::boost::is_same::type>::value - || ::boost::is_same::type>::value; + static const bool value = BOOST_OPTIONAL_TT_PRED(is_same)>) + || BOOST_OPTIONAL_TT_PRED(is_same)>); }; template @@ -123,52 +135,58 @@ public: typedef T* pointer_type; typedef T* pointer_const_type; - optional() BOOST_NOEXCEPT : ptr_() {} - optional(none_t) BOOST_NOEXCEPT : ptr_() {} + BOOST_CXX14_CONSTEXPR optional() BOOST_NOEXCEPT : ptr_() {} + BOOST_CXX14_CONSTEXPR optional(none_t) BOOST_NOEXCEPT : ptr_() {} template - explicit optional(const optional& rhs) BOOST_NOEXCEPT : ptr_(rhs.get_ptr()) {} - optional(const optional& rhs) BOOST_NOEXCEPT : ptr_(rhs.get_ptr()) {} + BOOST_CXX14_CONSTEXPR explicit optional(const optional& rhs) BOOST_NOEXCEPT : ptr_(rhs.get_ptr()) {} + BOOST_CXX14_CONSTEXPR optional(const optional& rhs) BOOST_NOEXCEPT : ptr_(rhs.get_ptr()) {} // the following two implement a 'conditionally explicit' constructor: condition is a hack for buggy compilers with screwed conversion construction from const int - template - explicit optional(U& rhs, BOOST_DEDUCED_TYPENAME boost::enable_if_c::value && detail::is_const_integral_bad_for_conversion::value, bool>::type = true) BOOST_NOEXCEPT + template ), + BOOST_OPTIONAL_REQUIRES(detail::is_const_integral_bad_for_conversion)> + BOOST_CXX14_CONSTEXPR explicit + optional(U& rhs) BOOST_NOEXCEPT : ptr_(boost::addressof(rhs)) {} - template - optional(U& rhs, BOOST_DEDUCED_TYPENAME boost::enable_if_c::value && !detail::is_const_integral_bad_for_conversion::value, bool>::type = true) BOOST_NOEXCEPT + template ), + BOOST_OPTIONAL_REQUIRES(!detail::is_const_integral_bad_for_conversion)> + BOOST_CXX14_CONSTEXPR + optional(U& rhs) BOOST_NOEXCEPT : ptr_(boost::addressof(rhs)) {} - optional& operator=(const optional& rhs) BOOST_NOEXCEPT { ptr_ = rhs.get_ptr(); return *this; } + BOOST_CXX14_CONSTEXPR optional& operator=(const optional& rhs) BOOST_NOEXCEPT { ptr_ = rhs.get_ptr(); return *this; } template - optional& operator=(const optional& rhs) BOOST_NOEXCEPT { ptr_ = rhs.get_ptr(); return *this; } - optional& operator=(none_t) BOOST_NOEXCEPT { ptr_ = 0; return *this; } + BOOST_CXX14_CONSTEXPR optional& operator=(const optional& rhs) BOOST_NOEXCEPT { ptr_ = rhs.get_ptr(); return *this; } + BOOST_CXX14_CONSTEXPR optional& operator=(none_t) BOOST_NOEXCEPT { ptr_ = 0; return *this; } - void swap(optional& rhs) BOOST_NOEXCEPT { std::swap(ptr_, rhs.ptr_); } - T& get() const { BOOST_ASSERT(ptr_); return *ptr_; } + BOOST_CXX14_CONSTEXPR void swap(optional& rhs) BOOST_NOEXCEPT { ::std::swap(ptr_, rhs.ptr_); } + BOOST_CXX14_CONSTEXPR T& get() const { BOOST_ASSERT(ptr_); return *ptr_; } - T* get_ptr() const BOOST_NOEXCEPT { return ptr_; } - T* operator->() const { BOOST_ASSERT(ptr_); return ptr_; } - T& operator*() const { BOOST_ASSERT(ptr_); return *ptr_; } + BOOST_CXX14_CONSTEXPR T* get_ptr() const BOOST_NOEXCEPT { return ptr_; } + BOOST_CXX14_CONSTEXPR T* operator->() const { BOOST_ASSERT(ptr_); return ptr_; } + BOOST_CXX14_CONSTEXPR T& operator*() const { BOOST_ASSERT(ptr_); return *ptr_; } - T& value() const + BOOST_CXX14_CONSTEXPR T& value() const { if (this->is_initialized()) return this->get(); else - throw_exception(bad_optional_access()); + boost::throw_exception(boost::bad_optional_access()); } - explicit operator bool() const BOOST_NOEXCEPT { return ptr_ != 0; } + BOOST_CXX14_CONSTEXPR explicit operator bool() const BOOST_NOEXCEPT { return ptr_ != 0; } - void reset() BOOST_NOEXCEPT { ptr_ = 0; } + BOOST_CXX14_CONSTEXPR void reset() BOOST_NOEXCEPT { ptr_ = 0; } - bool is_initialized() const BOOST_NOEXCEPT { return ptr_ != 0; } - bool has_value() const BOOST_NOEXCEPT { return ptr_ != 0; } + BOOST_CXX14_CONSTEXPR bool is_initialized() const BOOST_NOEXCEPT { return ptr_ != 0; } + BOOST_CXX14_CONSTEXPR bool has_value() const BOOST_NOEXCEPT { return ptr_ != 0; } template - optional::type> + BOOST_CXX14_CONSTEXPR optional::type> map(F f) const { if (this->has_value()) @@ -178,7 +196,7 @@ public: } template - optional::type> + BOOST_CXX14_CONSTEXPR optional::type> flat_map(F f) const { if (this->has_value()) @@ -189,86 +207,96 @@ public: #ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES - optional(T&& /* rhs */) BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); } + BOOST_CXX14_CONSTEXPR optional(T&& /* rhs */) BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); } - template - optional(R&& r, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR optional(R&& r) BOOST_NOEXCEPT : ptr_(boost::addressof(r)) { detail::prevent_binding_rvalue(); } - template - optional(bool cond, R&& r, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR optional(bool cond, R&& r) BOOST_NOEXCEPT : ptr_(cond ? boost::addressof(r) : 0) { detail::prevent_binding_rvalue(); } - template - BOOST_DEDUCED_TYPENAME boost::enable_if, optional&>::type + template )> + BOOST_CXX14_CONSTEXPR optional& operator=(R&& r) BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); ptr_ = boost::addressof(r); return *this; } - template - void emplace(R&& r, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR void emplace(R&& r) BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); ptr_ = boost::addressof(r); } - template - T& get_value_or(R&& r, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) const BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR T& get_value_or(R&& r) const BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); return ptr_ ? *ptr_ : r; } - template - T& value_or(R&& r, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) const BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR T& value_or(R&& r) const BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); return ptr_ ? *ptr_ : r; } - template - void reset(R&& r, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR void reset(R&& r) BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); ptr_ = boost::addressof(r); } template - T& value_or_eval(F f) const { return ptr_ ? *ptr_ : detail::forward_reference(f()); } + BOOST_CXX14_CONSTEXPR T& value_or_eval(F f) const { return ptr_ ? *ptr_ : detail::forward_reference(f()); } #else // BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // the following two implement a 'conditionally explicit' constructor - template - explicit optional(U& v, BOOST_DEDUCED_TYPENAME boost::enable_if_c::value && detail::is_const_integral_bad_for_conversion::value, bool>::type = true) BOOST_NOEXCEPT + template ), + BOOST_OPTIONAL_REQUIRES(detail::is_const_integral_bad_for_conversion)> + BOOST_CXX14_CONSTEXPR explicit optional(U& v) BOOST_NOEXCEPT : ptr_(boost::addressof(v)) { } - template - optional(U& v, BOOST_DEDUCED_TYPENAME boost::enable_if_c::value && !detail::is_const_integral_bad_for_conversion::value, bool>::type = true) BOOST_NOEXCEPT + template ), + BOOST_OPTIONAL_REQUIRES(!detail::is_const_integral_bad_for_conversion)> + BOOST_CXX14_CONSTEXPR optional(U& v) BOOST_NOEXCEPT : ptr_(boost::addressof(v)) { } - template - optional(bool cond, U& v, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) BOOST_NOEXCEPT : ptr_(cond ? boost::addressof(v) : 0) {} + template )> + BOOST_CXX14_CONSTEXPR optional(bool cond, U& v) BOOST_NOEXCEPT : ptr_(cond ? boost::addressof(v) : 0) {} - template - BOOST_DEDUCED_TYPENAME boost::enable_if, optional&>::type - operator=(U& v) BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR + optional& operator=(U& v) BOOST_NOEXCEPT { detail::prevent_assignment_from_false_const_integral(); ptr_ = boost::addressof(v); return *this; } - template - void emplace(U& v, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR void emplace(U& v) BOOST_NOEXCEPT { ptr_ = boost::addressof(v); } - template - T& get_value_or(U& v, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) const BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR T& get_value_or(U& v) const BOOST_NOEXCEPT { return ptr_ ? *ptr_ : v; } - template - T& value_or(U& v, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) const BOOST_NOEXCEPT - { return ptr_ ? *ptr_ : v; } + template )> + BOOST_CXX14_CONSTEXPR T& value_or(U& v) const BOOST_NOEXCEPT + { return ptr_ ? *ptr_ : v; } - template - void reset(U& v, BOOST_DEDUCED_TYPENAME boost::enable_if, bool>::type = true) BOOST_NOEXCEPT + template )> + BOOST_CXX14_CONSTEXPR void reset(U& v) BOOST_NOEXCEPT { ptr_ = boost::addressof(v); } template - T& value_or_eval(F f) const { return ptr_ ? *ptr_ : f(); } + BOOST_CXX14_CONSTEXPR T& value_or_eval(F f) const { return ptr_ ? *ptr_ : f(); } #endif // BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES }; template - void swap ( optional& x, optional& y) BOOST_NOEXCEPT + BOOST_CXX14_CONSTEXPR void swap ( optional& x, optional& y) BOOST_NOEXCEPT { x.swap(y); } diff --git a/include/boost/optional/detail/optional_relops.hpp b/include/boost/optional/detail/optional_relops.hpp index dc558ea..7f177f4 100644 --- a/include/boost/optional/detail/optional_relops.hpp +++ b/include/boost/optional/detail/optional_relops.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2003, 2008 Fernando Luis Cacciola Carballal. -// Copyright (C) 2015, 2024 Andrzej Krzemienski. +// Copyright (C) 2015, 2024, 2026 Andrzej Krzemienski. // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -25,32 +25,32 @@ namespace boost { // template -inline +inline BOOST_CXX14_CONSTEXPR bool operator == ( optional const& x, optional const& y ) { return bool(x) && bool(y) ? *x == *y : bool(x) == bool(y); } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator < ( optional const& x, optional const& y ) { return !y ? false : (!x ? true : (*x) < (*y)); } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator != ( optional const& x, optional const& y ) { return !( x == y ) ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator > ( optional const& x, optional const& y ) { return y < x ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator <= ( optional const& x, optional const& y ) { return !( y < x ) ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator >= ( optional const& x, optional const& y ) { return !( x < y ) ; } @@ -59,32 +59,32 @@ bool operator >= ( optional const& x, optional const& y ) // optional vs T cases // template -inline +inline BOOST_CXX14_CONSTEXPR bool operator == ( optional const& x, T const& y ) { return x && (*x == y); } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator < ( optional const& x, T const& y ) { return (!x) || (*x < y); } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator != ( optional const& x, T const& y ) { return !( x == y ) ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator > ( optional const& x, T const& y ) { return y < x ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator <= ( optional const& x, T const& y ) { return !( y < x ) ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator >= ( optional const& x, T const& y ) { return !( x < y ) ; } @@ -93,32 +93,32 @@ bool operator >= ( optional const& x, T const& y ) // template -inline +inline BOOST_CXX14_CONSTEXPR bool operator == ( T const& x, optional const& y ) { return y && (x == *y); } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator < ( T const& x, optional const& y ) { return y && (x < *y); } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator != ( T const& x, optional const& y ) { return !( x == y ) ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator > ( T const& x, optional const& y ) { return y < x ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator <= ( T const& x, optional const& y ) { return !( y < x ) ; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator >= ( T const& x, optional const& y ) { return !( x < y ) ; } @@ -128,33 +128,33 @@ bool operator >= ( T const& x, optional const& y ) // template -inline +inline BOOST_CXX14_CONSTEXPR bool operator == ( optional const& x, none_t ) BOOST_NOEXCEPT { return !x; } template -inline -bool operator < ( optional const&, none_t ) +inline BOOST_CXX14_CONSTEXPR +bool operator < ( optional const&, none_t ) BOOST_NOEXCEPT { return false; } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator != ( optional const& x, none_t ) BOOST_NOEXCEPT { return bool(x); } template -inline -bool operator > ( optional const& x, none_t y ) +inline BOOST_CXX14_CONSTEXPR +bool operator > ( optional const& x, none_t y ) BOOST_NOEXCEPT { return y < x ; } template -inline -bool operator <= ( optional const& x, none_t y ) +inline BOOST_CXX14_CONSTEXPR +bool operator <= ( optional const& x, none_t y ) BOOST_NOEXCEPT { return !( y < x ) ; } template -inline -bool operator >= ( optional const& x, none_t y ) +inline BOOST_CXX14_CONSTEXPR +bool operator >= ( optional const& x, none_t y ) BOOST_NOEXCEPT { return !( x < y ) ; } // @@ -162,33 +162,33 @@ bool operator >= ( optional const& x, none_t y ) // template -inline +inline BOOST_CXX14_CONSTEXPR bool operator == ( none_t , optional const& y ) BOOST_NOEXCEPT { return !y; } template -inline -bool operator < ( none_t , optional const& y ) +inline BOOST_CXX14_CONSTEXPR +bool operator < ( none_t , optional const& y ) BOOST_NOEXCEPT { return bool(y); } template -inline +inline BOOST_CXX14_CONSTEXPR bool operator != ( none_t, optional const& y ) BOOST_NOEXCEPT { return bool(y); } template -inline -bool operator > ( none_t x, optional const& y ) +inline BOOST_CXX14_CONSTEXPR +bool operator > ( none_t x, optional const& y ) BOOST_NOEXCEPT { return y < x ; } template -inline -bool operator <= ( none_t x, optional const& y ) +inline BOOST_CXX14_CONSTEXPR +bool operator <= ( none_t x, optional const& y ) BOOST_NOEXCEPT { return !( y < x ) ; } template -inline -bool operator >= ( none_t x, optional const& y ) +inline BOOST_CXX14_CONSTEXPR +bool operator >= ( none_t x, optional const& y ) BOOST_NOEXCEPT { return !( x < y ) ; } } // namespace boost diff --git a/include/boost/optional/detail/optional_swap.hpp b/include/boost/optional/detail/optional_swap.hpp index f1d301b..a9f88b7 100644 --- a/include/boost/optional/detail/optional_swap.hpp +++ b/include/boost/optional/detail/optional_swap.hpp @@ -54,7 +54,7 @@ struct swap_selector #endif #ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES -# define BOOST_OPTIONAL_DETAIL_MOVE(EXPR_) optional_detail::move(EXPR_) +# define BOOST_OPTIONAL_DETAIL_MOVE(EXPR_) optional_detail::move_(EXPR_) #else # define BOOST_OPTIONAL_DETAIL_MOVE(EXPR_) EXPR_ #endif diff --git a/include/boost/optional/detail/optional_trivially_copyable_base.hpp b/include/boost/optional/detail/optional_trivially_copyable_base.hpp index 3a08f7d..26f842a 100644 --- a/include/boost/optional/detail/optional_trivially_copyable_base.hpp +++ b/include/boost/optional/detail/optional_trivially_copyable_base.hpp @@ -54,7 +54,7 @@ class tc_optional_base : public optional_tag : m_initialized(false) { - construct(optional_detail::forward(expr),tag); + construct(optional_detail::forward_(expr),tag); } // tc_optional_base& operator= ( tc_optional_base const& ) = default; @@ -102,7 +102,7 @@ class tc_optional_base : public optional_tag template void assign_expr ( Expr&& expr, ExprPtr const* tag ) { - construct(optional_detail::forward(expr),tag); + construct(optional_detail::forward_(expr),tag); } #endif @@ -138,14 +138,14 @@ class tc_optional_base : public optional_tag template void construct ( in_place_init_t, Args&&... args ) { - m_storage = value_type( optional_detail::forward(args)... ) ; + m_storage = value_type( optional_detail::forward_(args)... ) ; m_initialized = true ; } template void emplace_assign ( Args&&... args ) { - construct(in_place_init, optional_detail::forward(args)...); + construct(in_place_init, optional_detail::forward_(args)...); } template @@ -153,7 +153,7 @@ class tc_optional_base : public optional_tag : m_initialized(false) { - construct(in_place_init, optional_detail::forward(args)...); + construct(in_place_init, optional_detail::forward_(args)...); } template @@ -162,7 +162,7 @@ class tc_optional_base : public optional_tag m_initialized(false) { if ( cond ) - construct(in_place_init, optional_detail::forward(args)...); + construct(in_place_init, optional_detail::forward_(args)...); } #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT @@ -207,7 +207,7 @@ class tc_optional_base : public optional_tag template void construct ( Expr&& expr, void const* ) { - m_storage = value_type(optional_detail::forward(expr)) ; + m_storage = value_type(optional_detail::forward_(expr)) ; m_initialized = true ; } @@ -218,7 +218,7 @@ class tc_optional_base : public optional_tag template void assign_expr_to_initialized ( Expr&& expr, void const* ) { - assign_value( optional_detail::forward(expr) ); + assign_value( optional_detail::forward_(expr) ); } #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION diff --git a/include/boost/optional/detail/optional_utility.hpp b/include/boost/optional/detail/optional_utility.hpp deleted file mode 100644 index f7c04ff..0000000 --- a/include/boost/optional/detail/optional_utility.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2024 Ryan Malcolm Underwood. -// -// Use, modification, and distribution is subject to the Boost Software -// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// See http://www.boost.org/libs/optional for documentation. -// -// You are welcome to contact the author at: -// typenametea@gmail.com - -#ifndef BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_RMU_06OCT2024_HPP -#define BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_RMU_06OCT2024_HPP - -namespace boost { -namespace optional_detail { - -// Workaround: forward and move aren't constexpr in C++11 -template -inline constexpr T&& forward(typename boost::remove_reference::type& t) noexcept -{ - return static_cast(t); -} - -template -inline constexpr T&& forward(typename boost::remove_reference::type&& t) noexcept -{ - static_assert(!boost::is_lvalue_reference::value, "Can not forward an rvalue as an lvalue."); - return static_cast(t); -} - -template -inline constexpr typename boost::remove_reference::type&& move(T&& t) noexcept -{ - return static_cast::type&&>(t); -} - -} // namespace optional_detail -} // namespace boost - -#endif diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index b8f402d..0b22845 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2003, 2008 Fernando Luis Cacciola Carballal. -// Copyright (C) 2014 - 2021 Andrzej Krzemienski. +// Copyright (C) 2014 - 2026 Andrzej Krzemieński. // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -7,23 +7,61 @@ // // See http://www.boost.org/libs/optional for documentation. // -// You are welcome to contact the author at: +// You are welcome to contact the authors at: // fernando_cacciola@hotmail.com +// akrzemi1@gmail.com +// +// You can file a GitHub issue at: +// https://github.com/boostorg/optional/issues // // Revisions: // 27 Apr 2008 (improved swap) Fernando Cacciola, Niels Dekker, Thorsten Ottosen // 05 May 2014 (Added move semantics) Andrzej Krzemienski +// 01 Feb 2026 Added constexpr implementaiton for C++14 // #ifndef BOOST_OPTIONAL_OPTIONAL_FLC_19NOV2002_HPP #define BOOST_OPTIONAL_OPTIONAL_FLC_19NOV2002_HPP -#include +#include + + #ifndef BOOST_NO_IOSTREAM #include + +namespace boost { + +// The following declaration prevents a bug where operator safe-bool is used upon streaming optional object if you forget the IO header. +template +std::basic_ostream& +operator<<(std::basic_ostream& os, optional_detail::optional_tag const&) +{ + static_assert(sizeof(CharType) == 0, "If you want to output boost::optional, include header "); + return os; +} + +} // namespace boost #endif // BOOST_NO_IOSTREAM + +#include + + +#if !defined(BOOST_NO_CXX14_CONSTEXPR) && \ + !defined(BOOST_NO_CXX11_REF_QUALIFIERS) && \ + !defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES) && \ + !defined(BOOST_NO_CXX11_UNRESTRICTED_UNION) +# define BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#endif + +#if defined BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#include +#else + + +#include + + #include -#include #include #include #include @@ -50,83 +88,20 @@ #include #include #include -#include + #include #include #include #include -#include -#include -namespace boost { namespace optional_detail { - -template -struct optional_value_type -{ -}; - -template -struct optional_value_type< ::boost::optional > -{ - typedef U type; -}; - -template -T declval(); - - -// implementing my own result_of so that it works for C++11 (std::result_of) -// and in C++20 (std::invoke_result). -template ()(declval()))> -struct result_of -{ - typedef Rslt type; -}; - -template ::type>::type> -struct result_value_type -{ - typedef Rslt type; -}; - -// optional()(optional_detail::declval()))>::type> - -}} // namespace boost::optional_detail namespace boost { -namespace optional_ns { - -// a tag for in-place initialization of contained value -struct in_place_init_t -{ - struct init_tag{}; - BOOST_CONSTEXPR explicit in_place_init_t(init_tag){} -}; -BOOST_INLINE_CONSTEXPR in_place_init_t in_place_init ((in_place_init_t::init_tag())); - -// a tag for conditional in-place initialization of contained value -struct in_place_init_if_t -{ - struct init_tag{}; - BOOST_CONSTEXPR explicit in_place_init_if_t(init_tag){} -}; -BOOST_INLINE_CONSTEXPR in_place_init_if_t in_place_init_if ((in_place_init_if_t::init_tag())); - -} // namespace optional_ns - -using optional_ns::in_place_init_t; -using optional_ns::in_place_init; -using optional_ns::in_place_init_if_t; -using optional_ns::in_place_init_if; - namespace optional_detail { struct init_value_tag {}; -struct optional_tag {}; - template class optional_base : public optional_tag @@ -177,7 +152,7 @@ class optional_base : public optional_tag : m_initialized(false) { - construct( optional_detail::move(val) ); + construct( optional_detail::move_(val) ); } // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. @@ -197,7 +172,7 @@ class optional_base : public optional_tag m_initialized(false) { if ( cond ) - construct(optional_detail::move(val)); + construct(optional_detail::move_(val)); } // Creates a deep copy of another optional @@ -218,7 +193,7 @@ class optional_base : public optional_tag m_initialized(false) { if ( rhs.is_initialized() ) - construct( optional_detail::move(rhs.get_impl()) ); + construct( optional_detail::move_(rhs.get_impl()) ); } @@ -227,7 +202,7 @@ class optional_base : public optional_tag : m_initialized(false) { - construct(optional_detail::forward(expr),tag); + construct(optional_detail::forward_(expr),tag); } optional_base& operator= ( optional_base const& rhs ) @@ -268,13 +243,13 @@ class optional_base : public optional_tag if (is_initialized()) { if ( rhs.is_initialized() ) - assign_value( optional_detail::move(rhs.get_impl()) ); + assign_value( optional_detail::move_(rhs.get_impl()) ); else destroy(); } else { if ( rhs.is_initialized() ) - construct(optional_detail::move(rhs.get_impl())); + construct(optional_detail::move_(rhs.get_impl())); } } @@ -334,8 +309,8 @@ class optional_base : public optional_tag void assign ( rval_reference_type val ) { if (is_initialized()) - assign_value( optional_detail::move(val) ); - else construct( optional_detail::move(val) ); + assign_value( optional_detail::move_(val) ); + else construct( optional_detail::move_(val) ); } // Assigns from "none", destroying the current value, if any, leaving this UNINITIALIZED @@ -348,8 +323,8 @@ class optional_base : public optional_tag void assign_expr ( Expr&& expr, ExprPtr const* tag ) { if (is_initialized()) - assign_expr_to_initialized(optional_detail::forward(expr),tag); - else construct(optional_detail::forward(expr),tag); + assign_expr_to_initialized(optional_detail::forward_(expr),tag); + else construct(optional_detail::forward_(expr),tag); } #endif @@ -381,7 +356,7 @@ class optional_base : public optional_tag void construct ( rval_reference_type val ) { - ::new (m_storage.address()) unqualified_value_type( optional_detail::move(val) ) ; + ::new (m_storage.address()) unqualified_value_type( optional_detail::move_(val) ) ; m_initialized = true ; } @@ -391,7 +366,7 @@ class optional_base : public optional_tag template void construct ( in_place_init_t, Args&&... args ) { - ::new (m_storage.address()) unqualified_value_type( optional_detail::forward(args)... ) ; + ::new (m_storage.address()) unqualified_value_type( optional_detail::forward_(args)... ) ; m_initialized = true ; } @@ -399,7 +374,7 @@ class optional_base : public optional_tag void emplace_assign ( Args&&... args ) { destroy(); - construct(in_place_init, optional_detail::forward(args)...); + construct(in_place_init, optional_detail::forward_(args)...); } template @@ -407,7 +382,7 @@ class optional_base : public optional_tag : m_initialized(false) { - construct(in_place_init, optional_detail::forward(args)...); + construct(in_place_init, optional_detail::forward_(args)...); } template @@ -416,7 +391,7 @@ class optional_base : public optional_tag m_initialized(false) { if ( cond ) - construct(in_place_init, optional_detail::forward(args)...); + construct(in_place_init, optional_detail::forward_(args)...); } #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT @@ -461,7 +436,7 @@ class optional_base : public optional_tag template void construct ( Expr&& expr, void const* ) { - new (m_storage.address()) unqualified_value_type(optional_detail::forward(expr)) ; + new (m_storage.address()) unqualified_value_type(optional_detail::forward_(expr)) ; m_initialized = true ; } @@ -472,7 +447,7 @@ class optional_base : public optional_tag template void assign_expr_to_initialized ( Expr&& expr, void const* ) { - assign_value( optional_detail::forward(expr) ); + assign_value( optional_detail::forward_(expr) ); } #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION @@ -497,7 +472,7 @@ class optional_base : public optional_tag { // An exception can be thrown here. // It it happens, THIS will be left uninitialized. - new (m_storage.address()) unqualified_value_type(optional_detail::move(expr.get())) ; + new (m_storage.address()) unqualified_value_type(optional_detail::move_(expr.get())) ; m_initialized = true ; } } @@ -548,16 +523,15 @@ struct has_dedicated_constructor {}; template -struct is_in_place_factory - : boost::disjunction< boost::is_base_of::type>, - boost::is_base_of::type> > +struct is_factory + : boost::disjunction< is_in_place_factory, is_typed_in_place_factory > {}; #if !defined(BOOST_OPTIONAL_DETAIL_NO_IS_CONSTRUCTIBLE_TRAIT) template struct is_factory_or_constructible_to_T - : boost::disjunction< is_in_place_factory, boost::is_constructible > + : boost::disjunction< is_factory, boost::is_constructible > {}; template @@ -597,7 +571,7 @@ struct is_opt_assignable : boost::is_convertible template struct is_factory_or_opt_assignable_to_T - : boost::disjunction< is_in_place_factory, is_opt_assignable > + : boost::disjunction< is_factory, is_opt_assignable > {}; template ::value> @@ -675,7 +649,7 @@ class optional // Creates an optional initialized with 'move(val)'. // Can throw if T::T(T &&) does - optional ( rval_reference_type val ) : base(optional_detail::init_value_tag(), optional_detail::forward(val)) + optional ( rval_reference_type val ) : base(optional_detail::init_value_tag(), optional_detail::forward_(val)) {} // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. @@ -684,7 +658,7 @@ class optional /// Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. // Can throw if T::T(T &&) does - optional ( bool cond, rval_reference_type val ) : base( cond, optional_detail::forward(val) ) + optional ( bool cond, rval_reference_type val ) : base( cond, optional_detail::forward_(val) ) {} // NOTE: MSVC needs templated versions first @@ -718,7 +692,7 @@ class optional base() { if ( rhs.is_initialized() ) - this->construct( optional_detail::move(rhs.get()) ); + this->construct( optional_detail::move_(rhs.get()) ); } #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT @@ -737,7 +711,7 @@ class optional explicit optional ( Expr&& expr, BOOST_DEDUCED_TYPENAME boost::enable_if< optional_detail::is_optional_val_init_candidate, bool>::type = true ) - : base(optional_detail::forward(expr),boost::addressof(expr)) + : base(optional_detail::forward_(expr),boost::addressof(expr)) {} #endif // !defined BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT @@ -757,7 +731,7 @@ class optional #else optional ( optional && rhs ) BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value) - : base( optional_detail::move(rhs) ) + : base( optional_detail::move_(rhs) ) {} #endif @@ -776,7 +750,7 @@ class optional BOOST_DEDUCED_TYPENAME boost::enable_if, optional&>::type operator= ( Expr&& expr ) { - this->assign_expr(optional_detail::forward(expr),boost::addressof(expr)); + this->assign_expr(optional_detail::forward_(expr),boost::addressof(expr)); return *this ; } @@ -798,7 +772,7 @@ class optional template optional& operator= ( optional && rhs ) { - this->assign(optional_detail::move(rhs)); + this->assign(optional_detail::move_(rhs)); return *this ; } @@ -834,7 +808,7 @@ class optional BOOST_DEDUCED_TYPENAME boost::enable_if::type>, optional&>::type operator= ( T_&& val ) { - this->assign( optional_detail::forward(val) ) ; + this->assign( optional_detail::forward_(val) ) ; return *this ; } @@ -851,7 +825,7 @@ class optional // Assigns from a T (deep-moves the rhs value) optional& operator= ( rval_reference_type val ) { - this->assign( optional_detail::move(val) ) ; + this->assign( optional_detail::move_(val) ) ; return *this ; } @@ -871,17 +845,17 @@ class optional template void emplace ( Args&&... args ) { - this->emplace_assign( optional_detail::forward(args)... ); + this->emplace_assign( optional_detail::forward_(args)... ); } template explicit optional ( in_place_init_t, Args&&... args ) - : base( in_place_init, optional_detail::forward(args)... ) + : base( in_place_init, optional_detail::forward_(args)... ) {} template explicit optional ( in_place_init_if_t, bool cond, Args&&... args ) - : base( in_place_init_if, cond, optional_detail::forward(args)... ) + : base( in_place_init_if, cond, optional_detail::forward_(args)... ) {} void swap( optional & arg ) @@ -915,7 +889,7 @@ class optional reference_type operator *() BOOST_OPTIONAL_REF_QUAL { return this->get() ; } #ifndef BOOST_NO_CXX11_REF_QUALIFIERS - reference_type_of_temporary_wrapper operator *() && { return optional_detail::move(this->get()) ; } + reference_type_of_temporary_wrapper operator *() && { return optional_detail::move_(this->get()) ; } #endif reference_const_type value() BOOST_OPTIONAL_CONST_REF_QUAL @@ -940,7 +914,7 @@ class optional if (this->is_initialized()) return get(); else - return optional_detail::forward(v); + return optional_detail::forward_(v); } template @@ -956,7 +930,7 @@ class optional reference_type_of_temporary_wrapper value() && { if (this->is_initialized()) - return optional_detail::move(this->get()) ; + return optional_detail::move_(this->get()) ; else throw_exception(bad_optional_access()); } @@ -965,16 +939,16 @@ class optional value_type value_or ( U&& v ) && { if (this->is_initialized()) - return optional_detail::move(get()); + return optional_detail::move_(get()); else - return optional_detail::forward(v); + return optional_detail::forward_(v); } template value_type value_or_eval ( F f ) && { if (this->is_initialized()) - return optional_detail::move(get()); + return optional_detail::move_(get()); else return f(); } @@ -1005,7 +979,7 @@ class optional optional::type> map(F f) && { if (this->has_value()) - return f(optional_detail::move(this->get())); + return f(optional_detail::move_(this->get())); else return none; } @@ -1037,7 +1011,7 @@ class optional flat_map(F f) && { if (this->has_value()) - return f(optional_detail::move(get())); + return f(optional_detail::move_(get())); else return none; } @@ -1048,128 +1022,34 @@ class optional explicit operator bool() const BOOST_NOEXCEPT { return this->has_value() ; } } ; - -template -class optional -{ - static_assert(sizeof(T) == 0, "Optional rvalue references are illegal."); -} ; - } // namespace boost + + + + +#include + +#endif // pre-C++14 impl + + +namespace boost { + + template + class optional + { + static_assert(sizeof(T) == 0, "Optional rvalue references are illegal."); + }; + +} + #ifndef BOOST_OPTIONAL_CONFIG_DONT_SPECIALIZE_OPTIONAL_REFS # include #endif -namespace boost { - - -template -inline -optional::type> make_optional ( T && v ) -{ - return optional::type>(optional_detail::forward(v)); -} - -// Returns optional(cond,v) -template -inline -optional::type> make_optional ( bool cond, T && v ) -{ - return optional::type>(cond,optional_detail::forward(v)); -} - - -// Returns a reference to the value if this is initialized, otherwise, the behaviour is UNDEFINED. -// No-throw -template -inline -BOOST_DEDUCED_TYPENAME optional::reference_const_type -get ( optional const& opt ) -{ - return opt.get() ; -} - -template -inline -BOOST_DEDUCED_TYPENAME optional::reference_type -get ( optional& opt ) -{ - return opt.get() ; -} - -// Returns a pointer to the value if this is initialized, otherwise, returns NULL. -// No-throw -template -inline -BOOST_DEDUCED_TYPENAME optional::pointer_const_type -get ( optional const* opt ) -{ - return opt->get_ptr() ; -} - -template -inline -BOOST_DEDUCED_TYPENAME optional::pointer_type -get ( optional* opt ) -{ - return opt->get_ptr() ; -} - -// Returns a reference to the value if this is initialized, otherwise, the behaviour is UNDEFINED. -// No-throw -template -inline -BOOST_DEDUCED_TYPENAME optional::reference_const_type -get_optional_value_or ( optional const& opt, BOOST_DEDUCED_TYPENAME optional::reference_const_type v ) -{ - return opt.get_value_or(v) ; -} - -template -inline -BOOST_DEDUCED_TYPENAME optional::reference_type -get_optional_value_or ( optional& opt, BOOST_DEDUCED_TYPENAME optional::reference_type v ) -{ - return opt.get_value_or(v) ; -} - -// Returns a pointer to the value if this is initialized, otherwise, returns NULL. -// No-throw -template -inline -BOOST_DEDUCED_TYPENAME optional::pointer_const_type -get_pointer ( optional const& opt ) -{ - return opt.get_ptr() ; -} - -template -inline -BOOST_DEDUCED_TYPENAME optional::pointer_type -get_pointer ( optional& opt ) -{ - return opt.get_ptr() ; -} - -} // namespace boost - -#ifndef BOOST_NO_IOSTREAM -namespace boost { - -// The following declaration prevents a bug where operator safe-bool is used upon streaming optional object if you forget the IO header. -template -std::basic_ostream& -operator<<(std::basic_ostream& os, optional_detail::optional_tag const&) -{ - static_assert(sizeof(CharType) == 0, "If you want to output boost::optional, include header "); - return os; -} - -} // namespace boost -#endif // BOOST_NO_IOSTREAM - +#include #include -#include +#include + #endif // header guard diff --git a/include/boost/optional/optional_fwd.hpp b/include/boost/optional/optional_fwd.hpp index faee253..d2f0913 100644 --- a/include/boost/optional/optional_fwd.hpp +++ b/include/boost/optional/optional_fwd.hpp @@ -23,7 +23,11 @@ namespace boost { template class optional ; // This forward is needed to refer to namespace scope swap from the member swap +#ifdef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +template BOOST_OPTIONAL_CXX20_CONSTEXPR void swap ( optional& , optional& ) ; +#else template void swap ( optional& , optional& ) ; +#endif // BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION template struct optional_swap_should_use_default_constructor ; @@ -31,11 +35,10 @@ template struct optional_swap_should_use_default_constructor ; template class optional ; -template void swap ( optional& , optional& ) BOOST_NOEXCEPT; +template BOOST_CXX14_CONSTEXPR void swap ( optional& , optional& ) BOOST_NOEXCEPT; #endif } // namespace boost #endif - diff --git a/include/boost/optional/optional_io.hpp b/include/boost/optional/optional_io.hpp index 85acfa1..c2b81db 100644 --- a/include/boost/optional/optional_io.hpp +++ b/include/boost/optional/optional_io.hpp @@ -63,7 +63,7 @@ operator>>(std::basic_istream& in, optional& v) { T x; in >> x; - v = optional_detail::move(x); + v = optional_detail::move_(x); } else { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index af8e831..4834734 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -37,6 +37,7 @@ project run optional_test.cpp : : : /boost/bind//boost_bind ; run optional_test_assign.cpp ; run optional_test_swap.cpp ; +compile optional_test_constexpr.cpp ; compile optional_test_wuninitialized.cpp ; run optional_test_conversions_from_U.cpp ; run optional_test_convert_from_T.cpp ; diff --git a/test/optional_test_constexpr.cpp b/test/optional_test_constexpr.cpp new file mode 100644 index 0000000..c521fb8 --- /dev/null +++ b/test/optional_test_constexpr.cpp @@ -0,0 +1,96 @@ +// Copyright (C) 2026, Andrzej Krzemieński. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com + +#include "boost/optional.hpp" + +#ifdef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION + +struct Record +{ + int i; + constexpr explicit Record(int i) : i(i) {} + constexpr int operator()() const { return i; } +}; + +namespace test_int +{ + constexpr boost::optional oN; + static_assert(!oN); + static_assert(oN == boost::none); + static_assert(oN <= boost::none); + static_assert(!(oN != boost::none)); + static_assert(oN == oN); + static_assert(!oN.has_value()); + static_assert(oN.value_or({}) == 0); + static_assert(oN.value_or(0) == 0); + static_assert(oN.value_or_eval(Record(9)) == 9); + constexpr boost::optional oNc = oN; + constexpr boost::optional oNd = boost::none; + constexpr boost::optional oNe = {}; + static_assert(oNc == oN); + static_assert(oNd == oN); + static_assert(oNe == oN); + + constexpr boost::optional o1 = 1; + constexpr boost::optional o2 {2}; + + static_assert(o1); + static_assert(o1.has_value()); + static_assert(o1 != boost::none); + static_assert(o1 != oN); + static_assert(o1 > oN); + static_assert(o1 >= oN); + static_assert(*o1 == 1); + static_assert(o1.value() == 1); + static_assert(o1.value_or(0) == 1); + static_assert(o1.value_or({}) == 1); + static_assert(o1.value_or_eval(Record(9)) == 1); + static_assert(o1 == 1); + static_assert(o2); + static_assert(o2 != o1); + static_assert(o2 > o1); + + constexpr bool test_reset() { + boost::optional o = 1; + assert(o); + o = boost::none; + assert(!o); + return o == boost::none; + } + + static_assert(test_reset()); +} + +namespace test_record +{ + constexpr boost::optional rN = boost::none; + constexpr boost::optional r1 = Record(1); + constexpr boost::optional r2 (boost::in_place_init, 2); + + static_assert(!rN); + static_assert(rN == boost::none); + static_assert(r1); + static_assert(r1 != boost::none); + static_assert(r2); + static_assert(rN.value_or(Record(9)).i == 9); + static_assert(r1->i == 1); + static_assert(r2.value().i == 2); +} + +namespace test_optional_ref +{ + constexpr int gi = 9; + constexpr boost::optional iref = gi; + static_assert(iref); + static_assert(*iref == 9); +} + +#endif // BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION diff --git a/test/optional_test_convert_assign.cpp b/test/optional_test_convert_assign.cpp index a067520..63b8924 100644 --- a/test/optional_test_convert_assign.cpp +++ b/test/optional_test_convert_assign.cpp @@ -13,6 +13,7 @@ #include "boost/core/lightweight_test.hpp" #include "boost/none.hpp" +#include "boost/type_traits/is_assignable.hpp" //#ifndef BOOST_OPTIONAL_NO_CONVERTING_COPY_CTOR diff --git a/test/optional_test_move.cpp b/test/optional_test_move.cpp index 5561d92..00cc719 100644 --- a/test/optional_test_move.cpp +++ b/test/optional_test_move.cpp @@ -75,13 +75,13 @@ void test_move_ctor_from_U() optional o1 ((OracleVal())); BOOST_TEST(o1); BOOST_TEST(o1->s == sValueMoveConstructed || o1->s == sMoveConstructed); - + OracleVal v1; optional o2 (v1); BOOST_TEST(o2); BOOST_TEST(o2->s == sValueCopyConstructed || o2->s == sCopyConstructed || o2->s == sMoveConstructed ); BOOST_TEST(v1.s == sIntConstructed); - + optional o3 (std::move(v1)); BOOST_TEST(o3); BOOST_TEST(o3->s == sValueMoveConstructed || o3->s == sMoveConstructed); @@ -93,13 +93,13 @@ void test_move_ctor_form_T() optional o1 ((Oracle())); BOOST_TEST(o1); BOOST_TEST(o1->s == sMoveConstructed); - + Oracle v1; optional o2 (v1); BOOST_TEST(o2); BOOST_TEST(o2->s == sCopyConstructed); BOOST_TEST(v1.s == sDefaultConstructed); - + optional o3 (std::move(v1)); BOOST_TEST(o3); BOOST_TEST(o3->s == sMoveConstructed); @@ -110,24 +110,24 @@ void test_move_ctor_from_optional_T() { optional o1; optional o2(std::move(o1)); - + BOOST_TEST(!o1); BOOST_TEST(!o2); - + optional o3((Oracle())); optional o4(std::move(o3)); BOOST_TEST(o3); BOOST_TEST(o4); BOOST_TEST(o3->s == sMovedFrom); BOOST_TEST(o4->s == sMoveConstructed); - + optional o5((optional())); BOOST_TEST(!o5); - + optional o6((optional(Oracle()))); BOOST_TEST(o6); BOOST_TEST(o6->s == sMoveConstructed); - + optional o7(o6); // does copy ctor from non-const lvalue compile? } @@ -137,13 +137,13 @@ void test_move_assign_from_U() o1 = boost::none; // test if additional assignments didn't break it o1 = OracleVal(); BOOST_TEST(o1); - - BOOST_TEST(o1->s == sValueMoveConstructed); - + + BOOST_TEST(o1->s == sValueMoveConstructed); + o1 = OracleVal(); BOOST_TEST(o1); - BOOST_TEST(o1->s == sMoveAssigned); - + BOOST_TEST(o1->s == sMoveAssigned || o1->s == sValueMoveAssigned); + OracleVal v1; optional o2; o2 = v1; @@ -152,9 +152,9 @@ void test_move_assign_from_U() BOOST_TEST(v1.s == sIntConstructed); o2 = v1; BOOST_TEST(o2); - BOOST_TEST(o2->s == sCopyAssigned || o2->s == sMoveAssigned); + BOOST_TEST(o2->s == sCopyAssigned || o2->s == sMoveAssigned || o2->s == sValueCopyAssigned); BOOST_TEST(v1.s == sIntConstructed); - + optional o3; o3 = std::move(v1); BOOST_TEST(o3); @@ -167,12 +167,12 @@ void test_move_assign_from_T() optional o1; o1 = Oracle(); BOOST_TEST(o1); - BOOST_TEST(o1->s == sMoveConstructed); - + BOOST_TEST(o1->s == sMoveConstructed); + o1 = Oracle(); BOOST_TEST(o1); - BOOST_TEST(o1->s == sMoveAssigned); - + BOOST_TEST(o1->s == sMoveAssigned); + Oracle v1; optional o2; o2 = v1; @@ -183,7 +183,7 @@ void test_move_assign_from_T() BOOST_TEST(o2); BOOST_TEST(o2->s == sCopyAssigned); BOOST_TEST(v1.s == sDefaultConstructed); - + optional o3; o3 = std::move(v1); BOOST_TEST(o3); @@ -203,13 +203,13 @@ void test_move_assign_from_optional_T() BOOST_TEST(o3->s == sMoveConstructed); BOOST_TEST(o1); BOOST_TEST(o1->s == sCopyConstructed); - + o2 = std::move(o3); BOOST_TEST(o3); BOOST_TEST(o3->s == sMovedFrom); BOOST_TEST(o2); BOOST_TEST(o2->s == sMoveConstructed); - + o2 = optional((Oracle())); BOOST_TEST(o2); BOOST_TEST(o2->s == sMoveAssigned); @@ -222,11 +222,11 @@ public: MoveOnly(int v) : val(v) {} MoveOnly(MoveOnly&& rhs) : val(rhs.val) { rhs.val = 0; } void operator=(MoveOnly&& rhs) {val = rhs.val; rhs.val = 0; } - + private: MoveOnly(MoveOnly const&); void operator=(MoveOnly const&); - + friend class MoveOnlyB; }; @@ -243,7 +243,7 @@ void test_with_move_only() BOOST_TEST(o4->val == 1); BOOST_TEST(o2); BOOST_TEST(o2->val == 0); - + o3 = std::move(o4); BOOST_TEST(o3); BOOST_TEST(o3->val == 1); @@ -260,7 +260,7 @@ public: void operator=(MoveOnlyB&& rhs) {val = rhs.val; rhs.val = 0; } MoveOnlyB(MoveOnly&& rhs) : val(rhs.val) { rhs.val = 0; } void operator=(MoveOnly&& rhs) {val = rhs.val; rhs.val = 0; } - + private: MoveOnlyB(MoveOnlyB const&); void operator=(MoveOnlyB const&); @@ -273,14 +273,14 @@ void test_move_assign_from_optional_U() optional a((MoveOnly(2))); optional b1; b1 = std::move(a); - + BOOST_TEST(b1); BOOST_TEST(b1->val == 2); BOOST_TEST(a); BOOST_TEST(a->val == 0); - + b1 = MoveOnly(4); - + BOOST_TEST(b1); BOOST_TEST(b1->val == 4); } @@ -289,14 +289,14 @@ void test_move_ctor_from_optional_U() { optional a((MoveOnly(2))); optional b1(std::move(a)); - + BOOST_TEST(b1); BOOST_TEST(b1->val == 2); BOOST_TEST(a); BOOST_TEST(a->val == 0); - + optional b2(( optional(( MoveOnly(4) )) )); - + BOOST_TEST(b2); BOOST_TEST(b2->val == 4); } @@ -306,7 +306,7 @@ void test_swap() optional a((MoveOnly(2))); optional b((MoveOnly(3))); swap(a, b); - + BOOST_TEST(a->val == 3); BOOST_TEST(b->val == 2); } @@ -317,12 +317,12 @@ void test_optional_ref_to_movables() optional orm = m; orm->val = 2; BOOST_TEST(m.val == 2); - + optional orm2 = orm; orm2->val = 1; BOOST_TEST(m.val == 1); BOOST_TEST(orm->val == 1); - + optional orm3 = std::move(orm); orm3->val = 4; BOOST_TEST(m.val == 4); diff --git a/test/optional_test_path_assignment.cpp b/test/optional_test_path_assignment.cpp index d8240d1..3304e78 100644 --- a/test/optional_test_path_assignment.cpp +++ b/test/optional_test_path_assignment.cpp @@ -11,6 +11,9 @@ #include "boost/optional/optional.hpp" +#include "boost/core/enable_if.hpp" +#include "boost/type_traits/is_constructible.hpp" + #ifdef BOOST_BORLANDC #pragma hdrstop #endif diff --git a/test/optional_test_sfinae_friendly_ctor.cpp b/test/optional_test_sfinae_friendly_ctor.cpp index 2eff2ff..bb39642 100644 --- a/test/optional_test_sfinae_friendly_ctor.cpp +++ b/test/optional_test_sfinae_friendly_ctor.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2014 Andrzej Krzemienski. +// Copyright (C) 2014, 2026 Andrzej Krzemienski. // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,6 +9,7 @@ // You are welcome to contact the author at: // akrzemi1@gmail.com +#include #include "boost/optional/optional.hpp" #ifdef BOOST_BORLANDC diff --git a/test/optional_test_static_properties.cpp b/test/optional_test_static_properties.cpp index 90cd643..6c089c6 100644 --- a/test/optional_test_static_properties.cpp +++ b/test/optional_test_static_properties.cpp @@ -20,6 +20,7 @@ #include "boost/type_traits/is_base_of.hpp" #include "boost/optional/detail/experimental_traits.hpp" +#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION #ifndef BOOST_OPTIONAL_DETAIL_NO_DEFAULTED_MOVE_FUNCTIONS struct PrivDefault @@ -135,12 +136,15 @@ void test_trivial_copyability() } #endif +#endif // BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION int main() { +#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION #ifndef BOOST_OPTIONAL_DETAIL_NO_DEFAULTED_MOVE_FUNCTIONS test_type_traits(); test_trivial_copyability(); +#endif #endif return boost::report_errors(); } diff --git a/test/optional_test_swap.cpp b/test/optional_test_swap.cpp index bdd629a..8d7b57b 100644 --- a/test/optional_test_swap.cpp +++ b/test/optional_test_swap.cpp @@ -210,6 +210,7 @@ namespace boost { // Compile time tweaking on whether or not swap should use the default constructor: // +#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION template <> struct optional_swap_should_use_default_constructor< optional_swap_test::class_whose_default_ctor_should_be_used> : true_type {} ; @@ -218,7 +219,7 @@ template <> struct optional_swap_should_use_default_constructor< template struct optional_swap_should_use_default_constructor< optional_swap_test::template_whose_default_ctor_should_be_used > : true_type {} ; - +#endif // BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION // // Specialization of boost::swap: @@ -352,15 +353,19 @@ void test_swap_member_function( T const* ) void test_swap_tweaking() { ( test_swap_function( ARG(optional_swap_test::class_without_default_ctor) ) ); +#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION + ( test_swap_function( ARG(optional_swap_test::class_whose_explicit_ctor_should_be_used) ) ); ( test_swap_function( ARG(optional_swap_test::class_whose_default_ctor_should_be_used) ) ); ( test_swap_function( ARG(optional_swap_test::class_whose_default_ctor_should_not_be_used) ) ); - ( test_swap_function( ARG(optional_swap_test::class_whose_explicit_ctor_should_be_used) ) ); ( test_swap_function( ARG(optional_swap_test::template_whose_default_ctor_should_be_used) ) ); +#endif ( test_swap_member_function( ARG(optional_swap_test::class_without_default_ctor) ) ); +#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION + ( test_swap_member_function( ARG(optional_swap_test::class_whose_explicit_ctor_should_be_used) ) ); ( test_swap_member_function( ARG(optional_swap_test::class_whose_default_ctor_should_be_used) ) ); ( test_swap_member_function( ARG(optional_swap_test::class_whose_default_ctor_should_not_be_used) ) ); - ( test_swap_member_function( ARG(optional_swap_test::class_whose_explicit_ctor_should_be_used) ) ); ( test_swap_member_function( ARG(optional_swap_test::template_whose_default_ctor_should_be_used) ) ); +#endif } int main()