diff --git a/doc/00_optional.qbk b/doc/00_optional.qbk index 53459c9..be64591 100644 --- a/doc/00_optional.qbk +++ b/doc/00_optional.qbk @@ -1,8 +1,8 @@ [library Boost.Optional - [quickbook 1.4] + [quickbook 1.7] [authors [Cacciola Carballal, Fernando Luis]] [copyright 2003-2007 Fernando Luis Cacciola Carballal] - [copyright 2014-2026 Andrzej Krzemieński] + [copyright 2014-2026 Andrzej Krzemieński] [category miscellaneous] [id optional] [dirname optional] diff --git a/doc/16_optional_references.qbk b/doc/16_optional_references.qbk index 10664d7..58d7964 100644 --- a/doc/16_optional_references.qbk +++ b/doc/16_optional_references.qbk @@ -27,7 +27,7 @@ rather than the reference itself. On compilers that do not conform to Standard C++ rules of reference binding, some operations on optional references are disabled in order to prevent subtle bugs. For more details see -[link boost_optional.dependencies_and_portability.optional_reference_binding Dependencies and Portability section]. +[link optional_reference_binding Dependencies and Portability section]. ] [heading Rvalue references] @@ -39,6 +39,8 @@ Rvalue references and lvalue references to const have the ability in C++ to exte [endsect] + +[#optional_ref_rebinding_semantics] [section Rebinding semantics for assignment of optional references] If you assign to an ['uninitialized ] `optional` the effect is to bind (for diff --git a/doc/17_in_place_factories.qbk b/doc/17_in_place_factories.qbk index fee5340..4c9a96d 100644 --- a/doc/17_in_place_factories.qbk +++ b/doc/17_in_place_factories.qbk @@ -1,5 +1,5 @@  -[section In-Place Factories] +[section:in_place_factories In-Place Factories][#boost_optional_factories] One of the typical problems with wrappers and containers is that their interfaces usually provide an operation to initialize or assign the @@ -138,4 +138,4 @@ The factories are implemented in the headers: __IN_PLACE_FACTORY_HPP__ and __TYP [caution The support for in-place factories is deprecated. Use constructor taking `in_place_init` tag and function `.emplace()` instead.] -[endsect] +[endsect:in_place_factories] diff --git a/doc/1A_type_requirements.qbk b/doc/1A_type_requirements.qbk index 214fcbf..6931aaf 100644 --- a/doc/1A_type_requirements.qbk +++ b/doc/1A_type_requirements.qbk @@ -8,7 +8,7 @@ The very minimum requirement of `optional` is that `T` is a complete type and assert(!o); // o.value(); // always throws -But this is practically useless. In order for `optional` to be able to do anything useful and offer all the spectrum of ways of accessing the contained value, `T` needs to have at least one accessible constructor. In that case you need to initialize the optional object with function `emplace()`, or if your compiler does not support it, resort to [link boost_optional.design.in_place_factories In-Place Factories]: +But this is practically useless. In order for `optional` to be able to do anything useful and offer all the spectrum of ways of accessing the contained value, `T` needs to have at least one accessible constructor. In that case you need to initialize the optional object with function `emplace()`, or if your compiler does not support it, resort to [link boost_optional_factories In-Place Factories]: optional o; o.emplace("T", "ctor", "params"); diff --git a/doc/21_ref_none.qbk b/doc/21_ref_none.qbk index 65eaa81..de4d246 100644 --- a/doc/21_ref_none.qbk +++ b/doc/21_ref_none.qbk @@ -2,7 +2,7 @@ Boost.Optional Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal - Copyright (c) 2015 Andrzej Krzemienski + Copyright (c) 2015 Andrzej Krzemieński Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -15,14 +15,25 @@ ``` namespace boost { -class none_t {/* see below */}; +class none_t +{ + friend constexpr bool operator==(none_t, none_t) = default; + /* see below */ +}; inline constexpr none_t none (/* see below */); } // namespace boost ``` -Class `none_t` is meant to serve as a tag for selecting appropriate overloads of from `optional`'s interface. It is an empty, trivially copyable class with disabled default constructor. +Class `none_t` is meant to serve as a tag for selecting appropriate overloads of from `optional`'s interface. +It is an empty class. + +It is [@https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable.html trivially copyable]. + +It is ['not] [@https://en.cppreference.com/w/cpp/named_req/DefaultConstructible default-constructible]. + +It models concept [@https://en.cppreference.com/w/cpp/concepts/equality_comparable `std::equality_comparable`]. Constant `none` is used to indicate an optional object that does not contain a value in initialization, assignment and relational operations of `optional`. diff --git a/doc/28_ref_optional_semantics.qbk b/doc/28_ref_optional_semantics.qbk index 8447d06..f377f5b 100644 --- a/doc/28_ref_optional_semantics.qbk +++ b/doc/28_ref_optional_semantics.qbk @@ -281,7 +281,7 @@ factory. * [*Postconditions: ] `*this` is [_initialized] and its value is ['directly given] from the factory `f` (i.e., the value [_is not copied]). * [*Throws:] Whatever the `T` constructor called by the factory throws. -* [*Notes:] See [link boost_optional.design.in_place_factories In-Place Factories] +* [*Notes:] See [link boost_optional_factories In-Place Factories] * [*Exception Safety:] Exceptions can only be thrown during the call to the `T` constructor used by the factory; in that case, this constructor has no effect. @@ -524,7 +524,7 @@ factory. * [*Postconditions: ] `*this` is [_initialized] and its value is ['directly given] from the factory `f` (i.e., the value [_is not copied]). * [*Throws:] Whatever the `T` constructor called by the factory throws. -* [*Notes:] See [link boost_optional.design.in_place_factories In-Place Factories] +* [*Notes:] See [link boost_optional_factories In-Place Factories] * [*Exception Safety:] Exceptions can only be thrown during the call to the `T` constructor used by the factory; in that case, the `optional` object will be reset to be ['uninitialized]. @@ -956,7 +956,7 @@ __SPACE__ * [*Postconditions:] `bool(*this) == bool(rhs)`. -* [*Notes:] This behaviour is called ['rebinding semantics]. See [link boost_optional.design.optional_references.rebinding_semantics_for_assignment_of_optional_references here] for details. +* [*Notes:] This behaviour is called ['rebinding semantics]. See [link optional_ref_rebinding_semantics here] for details. * [*Example:] `` diff --git a/doc/29_ref_optional_convenience.qbk b/doc/29_ref_optional_convenience.qbk index 63b4732..34584f1 100644 --- a/doc/29_ref_optional_convenience.qbk +++ b/doc/29_ref_optional_convenience.qbk @@ -11,7 +11,7 @@ [section Header ] -This is an alias for header [link boost_optional.reference.header__boost_optional_optional_hpp_.header_optional_optional ``]. +This is an alias for header [link ref_header_optional_optional_hpp ``]. [endsect] diff --git a/doc/90_dependencies.qbk b/doc/90_dependencies.qbk index 6409651..aeea15a 100644 --- a/doc/90_dependencies.qbk +++ b/doc/90_dependencies.qbk @@ -13,13 +13,55 @@ [section Minimum System Requirements] -This library requires C++11 as minimum. However some features are disabled. +This library requires C++11 as minimum. However, in C++11 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. +[section:constexpr Support for `constexpr`] + +[section C++11] +For compilers fully supporting C++11 (including unconstrained unions and ref-qualifiers), +for trivially-destructible `T`s, `optional` is a ['literal type] and its constructors +with `this->has_value() == false` as postcondition: + +* `optional()`, +* `optional(none_t)`, + +are ['core constant expressions]. Even for other `T`s, these constructors are guaranteed to +perform ['constant initialization]: they are never subject to "initialization order fiasco". + +Other constructors with `this->has_value() == true` +as postcondition are core constant expressions if the expression required to initialize +the contained value is a core constant expression. This includes constructors: + +* `template optional(in_place_init, Args&&...)`, +* `template optional(U&&)`, +* `optional(const T&)`, +* `optional(T&&)`. + +Other constructors, including the copy and move constructos, are ['not] core constant expressions. + +Member functions `.has_value`, `operator bool` and (non)equality comparisons with `none` are core-constant expressions for trivially-destructible `T`s. + +Also all `const`-qualified non-static member functions and comparison operators are core constant expressions, if the corresponding operations on `T` +are core constant expressions. +[endsect] + +[section C++14] +For C++14 and higher this library, for trivially-destructible `T`s provides the `constexpr` interface for all mutable and non-mutable +member functions, as long as: + +* the corresponding operations on `T` are core constant expressions and +* the member never attempts to change the `optional`'s state from not containing a value to containing a value. [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 C++17] +In C++17 all non-deprecated constructors are core constant expressions as long as +`T` is a literal type and its constructor used is a core constant expression. +[endsect] + +[endsect:constexpr] [endsect] diff --git a/doc/91_comparison_with_std.qbk b/doc/91_comparison_with_std.qbk index e72dd46..ed52fdc 100644 --- a/doc/91_comparison_with_std.qbk +++ b/doc/91_comparison_with_std.qbk @@ -11,8 +11,7 @@ [section:std_comp Comparison with `std::optional`] -[table - [] +[table Comparison with `std::optional` [ [[*`boost::optional`]] [[*`std::optional`]] [] ] [ [`optional o = none;`] [`optional o = nullopt;`] [Different name for no-value tag.] ] [ [`optional o {in_place_init, a, b};`] [`optional o {in_place, a, b};`] [Different name for in-place initialization tag.] ] @@ -42,6 +41,12 @@ [ [`make_optional(cond, v);`] [] [No `make_optional` with condition in `std`.] ] [ [] [`make_optional(a, b);`] [No `make_optional` with specified `T` in `boost`.] ] [ [`std::cout << optional{};`] [] [No printing to IOStreams in `std`.]] + + [ [`std::vector> rng;` + + `std::ranges::find(rng, boost::none)`] [`std::vector> rng;` + + `std::ranges::find(rng, boost::optional{})`] [optional is never [@https://en.cppreference.com/w/cpp/concepts/equality_comparable `std::equality_comparable_with`] `nullopt_t` in `std`. ] ] ] diff --git a/doc/92_relnotes.qbk b/doc/92_relnotes.qbk index baa34b6..e1c383b 100644 --- a/doc/92_relnotes.qbk +++ b/doc/92_relnotes.qbk @@ -11,9 +11,35 @@ [section:relnotes Release Notes] +[heading Boost Release 1.91] +* For compilers with full C++11 support (including "unrestricted unions" and ref-qualifiers) + changed the implementation from aligned storage to union storage. This enables the gradual + `constexpr` support: + * In C++11, some constructors and `const`-qualified accessors become usable + in compile-time contexts (are ['core constant expressions] for types + satisfying certain constraints. + * In C++14 even some mutating operations become core constant expressions (those + that do not require changing the state of `optional` from not having a value to + having a value), for co-operating types. + * In C++17 all constructors (including copy and move) become core constant expressions + for co-operating types. + This addresses [@https://github.com/boostorg/optional/issues/143 issue #143]. + +* *Breaking change.* In the said implementation, abandoned the mechanism for customizing + `swap`. Hardly anyone knows about this mechanism and it was never documented. + +* In the said implementation, 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`. + +* `none_t` is now `std::equality_comparable`, which means that `none_t` and `optional` + model concept `std::equality_comparable_with` (for `std::equality_comparable` `T`s), + which means that you can `std::ranges::find(rng, boost::none)` for a range of optional objects. [heading Boost Release 1.87] diff --git a/include/boost/none_t.hpp b/include/boost/none_t.hpp index 1c8e076..8815121 100644 --- a/include/boost/none_t.hpp +++ b/include/boost/none_t.hpp @@ -25,7 +25,7 @@ #elif defined(BOOST_NO_CXX11_REF_QUALIFIERS) || defined(BOOST_NO_CXX11_NOEXCEPT) || defined(BOOST_NO_CXX11_DEFAULTED_MOVES) -BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Optional 1.83 and will be removed in Boost.Optional 1.88.") +BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Optional 1.83 and will be removed in Boost.Optional 1.92.") #endif @@ -46,6 +46,11 @@ struct none_t { struct init_tag{}; explicit BOOST_CONSTEXPR none_t(init_tag){} // to disable default constructor + +#ifndef BOOST_OPTIONAL_DISABLE_EQUALITY_FOR_NONE + friend BOOST_CONSTEXPR bool operator==(none_t, none_t) { return true; } + friend BOOST_CONSTEXPR bool operator!=(none_t, none_t) { return false; } +#endif }; #endif // old implementation workarounds diff --git a/include/boost/optional/detail/optional_common_defs.hpp b/include/boost/optional/detail/optional_common_defs.hpp index 2e92dee..0ad5b35 100644 --- a/include/boost/optional/detail/optional_common_defs.hpp +++ b/include/boost/optional/detail/optional_common_defs.hpp @@ -22,14 +22,22 @@ #include #include #include +#include -#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#ifndef BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION #include #include #endif +// This is needed for C++11, where constexpr functions must contain a single expression. +// We want to assert and then return. +#if defined NDEBUG +# define BOOST_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) +#else +# define BOOST_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([]{BOOST_ASSERT(!(#CHECK));}(), (EXPR))) +#endif -#ifndef BOOST_NO_CXX14_CONSTEXPR +#ifdef BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION # define BOOST_OPTIONAL_DECAY(T) typename ::std::decay::type # define BOOST_OPTIONAL_IS_TAGGED(TAG, U) ::std::is_base_of #else diff --git a/include/boost/optional/detail/optional_reference_spec.hpp b/include/boost/optional/detail/optional_reference_spec.hpp index f0956e9..4471ab0 100644 --- a/include/boost/optional/detail/optional_reference_spec.hpp +++ b/include/boost/optional/detail/optional_reference_spec.hpp @@ -13,13 +13,13 @@ #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 +#ifndef BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION #include #include #endif #endif -#ifdef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#ifdef BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION # define BOOST_OPTIONAL_TT_PREFIX ::std #else # define BOOST_OPTIONAL_TT_PREFIX boost @@ -135,27 +135,27 @@ public: typedef T* pointer_type; typedef T* pointer_const_type; - BOOST_CXX14_CONSTEXPR optional() BOOST_NOEXCEPT : ptr_() {} - BOOST_CXX14_CONSTEXPR optional(none_t) BOOST_NOEXCEPT : ptr_() {} + BOOST_CONSTEXPR optional() BOOST_NOEXCEPT : ptr_() {} + BOOST_CONSTEXPR optional(none_t) BOOST_NOEXCEPT : ptr_() {} template - 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()) {} + BOOST_CONSTEXPR explicit optional(const optional& rhs) BOOST_NOEXCEPT : ptr_(rhs.get_ptr()) {} + BOOST_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 ), BOOST_OPTIONAL_REQUIRES(detail::is_const_integral_bad_for_conversion)> - BOOST_CXX14_CONSTEXPR explicit - optional(U& rhs) BOOST_NOEXCEPT - : ptr_(boost::addressof(rhs)) {} + BOOST_CONSTEXPR explicit + optional(U& rhs) BOOST_NOEXCEPT + : ptr_(boost::addressof(rhs)) {} template ), BOOST_OPTIONAL_REQUIRES(!detail::is_const_integral_bad_for_conversion)> - BOOST_CXX14_CONSTEXPR - optional(U& rhs) BOOST_NOEXCEPT - : ptr_(boost::addressof(rhs)) {} + BOOST_CONSTEXPR + optional(U& rhs) BOOST_NOEXCEPT + : ptr_(boost::addressof(rhs)) {} BOOST_CXX14_CONSTEXPR optional& operator=(const optional& rhs) BOOST_NOEXCEPT { ptr_ = rhs.get_ptr(); return *this; } template @@ -164,50 +164,47 @@ public: 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_; } + constexpr T& get() const { return BOOST_OPTIONAL_ASSERTED_EXPRESSION(ptr_, *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_; } + constexpr T* get_ptr() const BOOST_NOEXCEPT { return ptr_; } + constexpr T* operator->() const { return BOOST_OPTIONAL_ASSERTED_EXPRESSION(ptr_, ptr_); } + constexpr T& operator*() const { return BOOST_OPTIONAL_ASSERTED_EXPRESSION(ptr_, *ptr_); } - BOOST_CXX14_CONSTEXPR T& value() const + constexpr T& value() const { - if (this->is_initialized()) - return this->get(); - else - boost::throw_exception(boost::bad_optional_access()); + return this->is_initialized() ? + this->get() : + (boost::throw_exception(boost::bad_optional_access()), this->get()); } - BOOST_CXX14_CONSTEXPR explicit operator bool() const BOOST_NOEXCEPT { return ptr_ != 0; } + constexpr explicit operator bool() const BOOST_NOEXCEPT { return ptr_ != 0; } BOOST_CXX14_CONSTEXPR void reset() BOOST_NOEXCEPT { 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; } + constexpr bool is_initialized() const BOOST_NOEXCEPT { return ptr_ != 0; } + constexpr bool has_value() const BOOST_NOEXCEPT { return ptr_ != 0; } template - BOOST_CXX14_CONSTEXPR optional::type> + constexpr optional::type> map(F f) const { - if (this->has_value()) - return f(this->get()); - else - return none; + return this->has_value() ? + f(get()) : + optional::type>(); } template - BOOST_CXX14_CONSTEXPR optional::type> + constexpr optional::type> flat_map(F f) const { - if (this->has_value()) - return f(get()); - else - return none; + return this->has_value() ? + f(get()) : + optional::type>(); } #ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES - BOOST_CXX14_CONSTEXPR optional(T&& /* rhs */) BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); } + optional(T&& /* rhs */) BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); } template )> BOOST_CXX14_CONSTEXPR optional(R&& r) BOOST_NOEXCEPT diff --git a/include/boost/optional/detail/optional_relops.hpp b/include/boost/optional/detail/optional_relops.hpp index 7f177f4..aaa8133 100644 --- a/include/boost/optional/detail/optional_relops.hpp +++ b/include/boost/optional/detail/optional_relops.hpp @@ -25,32 +25,32 @@ namespace boost { // template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator == ( optional const& x, optional const& y ) { return bool(x) && bool(y) ? *x == *y : bool(x) == bool(y); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator < ( optional const& x, optional const& y ) { return !y ? false : (!x ? true : (*x) < (*y)); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator != ( optional const& x, optional const& y ) { return !( x == y ) ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator > ( optional const& x, optional const& y ) { return y < x ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator <= ( optional const& x, optional const& y ) { return !( y < x ) ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_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 BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator == ( optional const& x, T const& y ) { return x && (*x == y); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator < ( optional const& x, T const& y ) { return (!x) || (*x < y); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator != ( optional const& x, T const& y ) { return !( x == y ) ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator > ( optional const& x, T const& y ) { return y < x ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator <= ( optional const& x, T const& y ) { return !( y < x ) ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_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 BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator == ( T const& x, optional const& y ) { return y && (x == *y); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator < ( T const& x, optional const& y ) { return y && (x < *y); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator != ( T const& x, optional const& y ) { return !( x == y ) ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator > ( T const& x, optional const& y ) { return y < x ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator <= ( T const& x, optional const& y ) { return !( y < x ) ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator >= ( T const& x, optional const& y ) { return !( x < y ) ; } @@ -128,32 +128,32 @@ bool operator >= ( T const& x, optional const& y ) // template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator == ( optional const& x, none_t ) BOOST_NOEXCEPT { return !x; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator < ( optional const&, none_t ) BOOST_NOEXCEPT { return false; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator != ( optional const& x, none_t ) BOOST_NOEXCEPT { return bool(x); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator > ( optional const& x, none_t y ) BOOST_NOEXCEPT { return y < x ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator <= ( optional const& x, none_t y ) BOOST_NOEXCEPT { return !( y < x ) ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator >= ( optional const& x, none_t y ) BOOST_NOEXCEPT { return !( x < y ) ; } @@ -162,32 +162,32 @@ bool operator >= ( optional const& x, none_t y ) BOOST_NOEXCEPT // template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator == ( none_t , optional const& y ) BOOST_NOEXCEPT { return !y; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator < ( none_t , optional const& y ) BOOST_NOEXCEPT { return bool(y); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator != ( none_t, optional const& y ) BOOST_NOEXCEPT { return bool(y); } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator > ( none_t x, optional const& y ) BOOST_NOEXCEPT { return y < x ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator <= ( none_t x, optional const& y ) BOOST_NOEXCEPT { return !( y < x ) ; } template -inline BOOST_CXX14_CONSTEXPR +inline BOOST_CONSTEXPR bool operator >= ( none_t x, optional const& y ) BOOST_NOEXCEPT { return !( x < y ) ; } diff --git a/include/boost/optional/detail/optional_select_implementation.hpp b/include/boost/optional/detail/optional_select_implementation.hpp new file mode 100644 index 0000000..8a69eea --- /dev/null +++ b/include/boost/optional/detail/optional_select_implementation.hpp @@ -0,0 +1,36 @@ +// 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_OPTIONAL_SELECT_IMPLEMENTATION_01FEB2026_HPP +#define BOOST_OPTIONAL_DETAIL_OPTIONAL_SELECT_IMPLEMENTATION_01FEB2026_HPP + +#include + +#if !defined(BOOST_NO_CXX11_CONSTEXPR) && \ + !defined(BOOST_NO_CXX11_REF_QUALIFIERS) && \ + !defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES) && \ + !defined(BOOST_NO_CXX11_UNRESTRICTED_UNION) && \ + !defined(BOOST_NO_CXX11_NOEXCEPT) && \ + !defined(BOOST_NO_CXX11_DEFAULTED_MOVES) +# define BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION +#endif + + +// 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 + +#endif //BOOST_OPTIONAL_DETAIL_OPTIONAL_SELECT_IMPLEMENTATION_01FEB2026_HPP diff --git a/include/boost/optional/detail/constexpr_optional.hpp b/include/boost/optional/detail/union_optional.hpp similarity index 63% rename from include/boost/optional/detail/constexpr_optional.hpp rename to include/boost/optional/detail/union_optional.hpp index f7a9c1f..4993f58 100644 --- a/include/boost/optional/detail/constexpr_optional.hpp +++ b/include/boost/optional/detail/union_optional.hpp @@ -13,13 +13,12 @@ // 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 +#ifndef BOOST_OPTIONAL_DETAIL_UNION_OPTIONAL_01FEB2026_HPP +#define BOOST_OPTIONAL_DETAIL_UNION_OPTIONAL_01FEB2026_HPP -#include +//#include #include #include -#include #include #include @@ -30,12 +29,20 @@ # 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 +# ifdef __cpp_guaranteed_copy_elision +# if __cpp_guaranteed_copy_elision +# define BOOST_OPTIONAL_CONSTEXPR_COPY +# endif +# endif +template +struct fail_hard_on_nonconvertible +{ + static_assert(::std::is_convertible::value, "The argument must be convertible to T"); + using type = bool; +}; + // Missing C++17 type traits namespace boost { namespace optional_detail { @@ -47,7 +54,7 @@ struct conjunction : B1 {}; template struct conjunction - : ::std::conditional_t, B1> {}; + : ::std::conditional, B1>::type {}; }} @@ -108,13 +115,13 @@ struct constexpr_guarded_storage 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)...) {} + // 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 void reset () noexcept { init_ = false; } + BOOST_CXX14_CONSTEXPR void reset () noexcept { init_ = false; } - ~constexpr_guarded_storage() = default; + //~constexpr_guarded_storage() = default; }; @@ -130,12 +137,12 @@ struct fallback_guarded_storage 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) + template explicit constexpr 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)...) {} + // template >)> + // explicit fallback_guarded_storage(optional_ns::in_place_init_t, ::std::initializer_list il, Args&&... args) + // : init_(true), storage_(il, forward_(args)...) {} void reset() noexcept { @@ -169,16 +176,16 @@ namespace boost { { 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" ); + static_assert( !::std::is_same::type, none_t>::value, "optional is illegal" ); + static_assert( !::std::is_same::type, in_place_init_t>::value, "optional is illegal" ); + static_assert( !::std::is_same::type, in_place_init_if_t>::value, "optional is illegal" ); - - constexpr typename ::std::remove_const::type* dataptr() { return ::boost::addressof(storage.storage_.value_); } + BOOST_CXX14_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_; } + BOOST_CXX14_CONSTEXPR T&& contained_val() && { return optional_detail::move_(storage.storage_.value_); } + BOOST_CXX14_CONSTEXPR T& contained_val() & { return storage.storage_.value_; } template BOOST_OPTIONAL_CXX20_CONSTEXPR void initialize(Args&&... args) @@ -188,6 +195,31 @@ namespace boost { storage.init_ = true; } + #ifdef BOOST_OPTIONAL_CONSTEXPR_COPY + // The conditional initialization of storage needs to employ a factory + // function, so that we can utilize the guaranteed copy elision on the + // return type. + // an alternative -- using a conditional operator in the initializer -- + // does not work in MSVC 14.2 and 14.3. + template + static constexpr storage_t conditional_storage_from_values(bool cond, U&&... v) + { + if (cond) + return storage_t(in_place_init, optional_detail::forward_(v)...); + else + return storage_t(); + } + + template + static constexpr storage_t conditional_storage_from_optional(OU&& ou) + { + if (ou.has_value()) + return storage_t(in_place_init, *optional_detail::forward_(ou)); + else + return storage_t(); + } + #endif + public: using value_type = T; using unqualified_value_type = typename ::std::remove_const::type; @@ -209,36 +241,97 @@ namespace boost { constexpr optional(const T& v) : storage(v) {} constexpr optional(T&& v) : storage(optional_detail::move_(v)) {} +#ifdef BOOST_OPTIONAL_CONSTEXPR_COPY constexpr optional(bool cond, const T& v) - : storage(cond ? storage_t(v) : storage_t()) + : storage(conditional_storage_from_values(cond, v)) {} constexpr optional(bool cond, T&& v) - : storage(cond ? storage_t(optional_detail::move_(v)) : storage_t()) + : storage(conditional_storage_from_values(cond, optional_detail::move_(v))) {} constexpr optional(const optional& rhs) - : storage(rhs.is_initialized() ? storage_t(*rhs) : storage_t()) + : storage(conditional_storage_from_optional(rhs)) {} constexpr optional(optional&& rhs) noexcept(::std::is_nothrow_move_constructible::value) - : storage(rhs.is_initialized() ? storage_t(*optional_detail::move_(rhs)) : storage_t()) + : storage(conditional_storage_from_optional(optional_detail::move_(rhs))) {} template )> constexpr explicit optional(optional const& rhs) - : storage(rhs.is_initialized() ? storage_t(*rhs) : storage_t()) + : storage(conditional_storage_from_optional(rhs)) {} template )> constexpr explicit optional(optional && rhs) - : storage(rhs.is_initialized() ? storage_t(*optional_detail::move_(rhs)) : storage_t()) + : storage(conditional_storage_from_optional(optional_detail::move_(rhs))) {} + template + constexpr explicit optional( in_place_init_if_t, bool cond, Args&&... args ) + : storage(conditional_storage_from_values(cond, optional_detail::forward_(args)...)) + {} +#else + optional(bool cond, const T& v) + : storage() + { + if (cond) + initialize(v); + } + + optional(bool cond, T&& v) + : storage() + { + if (cond) + initialize(optional_detail::move_(v)); + } + + optional(const optional& rhs) + : storage() + { + if (rhs.is_initialized()) + initialize(*rhs); + } + + optional(optional&& rhs) + noexcept(::std::is_nothrow_move_constructible::value) + : storage() + { + if (rhs.is_initialized()) + initialize(*optional_detail::move_(rhs)); + } + + template )> + explicit optional(optional const& rhs) + : storage() + { + if (rhs.is_initialized()) + initialize(*rhs); + } + + template )> + explicit optional(optional && rhs) + : storage() + { + if (rhs.is_initialized()) + initialize(*optional_detail::move_(rhs)); + } + + template + explicit optional( in_place_init_if_t, bool cond, Args&&... args ) + : storage() + { + if (cond) + initialize(optional_detail::forward_(args)...); + } +#endif // BOOST_OPTIONAL_CONSTEXPR_COPY + template )> - constexpr explicit optional (FT&& factory) + /*non-constexpr (deprecated)*/ + explicit optional (FT&& factory) : storage() { factory.apply(this->dataptr()); @@ -247,10 +340,11 @@ namespace boost { template )> - constexpr explicit optional (FT&& factory) + /*non-constexpr (deprecated)*/ + explicit optional (FT&& factory) : storage() { - factory.template apply(this->dataptr()); + boost_optional_detail::construct(factory, this->dataptr()); storage.init_ = true; } @@ -265,12 +359,7 @@ namespace boost { : 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 + BOOST_CXX14_CONSTEXPR void reset() noexcept { storage.reset(); } @@ -283,13 +372,13 @@ namespace boost { template BOOST_OPTIONAL_CXX20_CONSTEXPR void emplace(Args&&... args) { - reset(); static_assert(noexcept(reset())); + reset(); // <-- now we are not containing a value initialize(optional_detail::forward_(args)...); } - constexpr optional& operator=(none_t) noexcept + BOOST_CXX14_CONSTEXPR optional& operator=(none_t) noexcept { reset(); return *this; @@ -386,15 +475,17 @@ namespace boost { } template )> + /*non-constexpr (deprecated)*/ optional& operator=(F&& factory) { reset(); - factory.template apply(this->dataptr()); + boost_optional_detail::construct(factory, this->dataptr()); storage.init_ = true; return *this; } template )> + /*non-constexpr (deprecated)*/ optional& operator=(F&& factory) { reset(); @@ -406,7 +497,6 @@ namespace boost { 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()) { @@ -428,8 +518,8 @@ namespace boost { 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(); } + constexpr reference_const_type get() const { return BOOST_OPTIONAL_ASSERTED_EXPRESSION(this->is_initialized(), this->contained_val()); } + BOOST_CXX14_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; } @@ -440,22 +530,19 @@ namespace boost { 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 reference_const_type operator*() const& { return this->get(); } + BOOST_CXX14_CONSTEXPR reference_type operator*() & { return this->get(); } + BOOST_CXX14_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 pointer_const_type operator->() const { return BOOST_OPTIONAL_ASSERTED_EXPRESSION(this->is_initialized(), this->dataptr()); } + BOOST_CXX14_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()); + return this->is_initialized() ? this->get() : (boost::throw_exception(boost::bad_optional_access()), this->get()); } - constexpr reference_type value() & + BOOST_CXX14_CONSTEXPR reference_type value() & { if (this->is_initialized()) return this->get(); @@ -463,7 +550,7 @@ namespace boost { boost::throw_exception(boost::bad_optional_access()); } - constexpr reference_type_of_temporary_wrapper value() && + BOOST_CXX14_CONSTEXPR reference_type_of_temporary_wrapper value() && { if (this->is_initialized()) return optional_detail::move_(this->get()); @@ -471,17 +558,15 @@ namespace boost { boost::throw_exception(boost::bad_optional_access()); } - template + template ::type, + typename fail_hard_on_nonconvertible::type = true> constexpr value_type value_or(U&& v) const& { - if (this->is_initialized()) - return get(); - else - return optional_detail::forward_(v); + return this->is_initialized() ? get() : T(optional_detail::forward_(v)); } - template - constexpr value_type value_or(U&& v) && + template ::type> + BOOST_CXX14_CONSTEXPR value_type value_or(U&& v) && { if (this->is_initialized()) return optional_detail::move_(get()); @@ -489,17 +574,15 @@ namespace boost { return optional_detail::forward_(v); } - template + template ()())>::type = true> constexpr value_type value_or_eval(F f) const& { - if (this->is_initialized()) - return get(); - else - return f(); + return this->is_initialized() ? get() : value_type(f()); } template - constexpr value_type value_or_eval ( F f ) && + BOOST_CXX14_CONSTEXPR value_type value_or_eval ( F f ) && { if (this->is_initialized()) return optional_detail::move_(get()); @@ -508,7 +591,8 @@ namespace boost { } template - constexpr optional::type> map(F f) & + BOOST_CXX14_CONSTEXPR optional::type> + map(F f) & { if (this->has_value()) return f(get()); @@ -517,16 +601,17 @@ namespace boost { } template - constexpr optional::type> map(F f) const& + constexpr optional::type> + map(F f) const& { - if (this->has_value()) - return f(get()); - else - return none; + return this->has_value() ? + f(get()) : + optional::type>(); } template - constexpr optional::type> map(F f) && + BOOST_CXX14_CONSTEXPR optional::type> + map(F f) && { if (this->has_value()) return f(optional_detail::move_(this->get())); @@ -535,7 +620,7 @@ namespace boost { } template - constexpr optional::type> + BOOST_CXX14_CONSTEXPR optional::type> flat_map(F f) & { if (this->has_value()) @@ -548,14 +633,13 @@ namespace boost { constexpr optional::type> flat_map(F f) const& { - if (this->has_value()) - return f(get()); - else - return none; + return this->has_value() ? + f(get()) : + optional::type>(); } template - constexpr optional::type> + BOOST_CXX14_CONSTEXPR optional::type> flat_map(F f) && { if (this->has_value()) @@ -577,6 +661,4 @@ namespace boost { } -// https://godbolt.org/z/ov1ahMsv6 - -#endif // BOOST_OPTIONAL_DETAIL_CONSTEXPR_OPTIONAL_01FEB2026_HPP +#endif // BOOST_OPTIONAL_DETAIL_UNION_OPTIONAL_01FEB2026_HPP diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 73ddeae..9bdd53d 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -23,15 +23,7 @@ #define BOOST_OPTIONAL_OPTIONAL_FLC_19NOV2002_HPP -#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) -# ifdef BOOST_OPTIONAL_CONFIG_ENABLE_CONSTEXPR_IMPLEMENTATION // for now, only per manual request -# define BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION -# endif -#endif - +#include #include @@ -56,8 +48,8 @@ operator<<(std::basic_ostream& os, optional_detail::optiona #include -#if defined BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION -#include +#if defined BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION +#include #else @@ -92,10 +84,8 @@ operator<<(std::basic_ostream& os, optional_detail::optiona #include #include - #include #include -#include #include diff --git a/include/boost/optional/optional_fwd.hpp b/include/boost/optional/optional_fwd.hpp index d2f0913..cfccb34 100644 --- a/include/boost/optional/optional_fwd.hpp +++ b/include/boost/optional/optional_fwd.hpp @@ -17,25 +17,32 @@ #define BOOST_OPTIONAL_OPTIONAL_FWD_FLC_19NOV2002_HPP #include +#include +#include +#include + 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& ) ; +#ifdef BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION + 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))); #else -template void swap ( optional& , optional& ) ; -#endif // BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION + template void swap ( optional& , optional& ) ; +#endif // BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION + template struct optional_swap_should_use_default_constructor ; + #ifndef BOOST_OPTIONAL_CONFIG_DONT_SPECIALIZE_OPTIONAL_REFS -template class optional ; + template class optional ; -template BOOST_CXX14_CONSTEXPR void swap ( optional& , optional& ) BOOST_NOEXCEPT; + template BOOST_CXX14_CONSTEXPR void swap ( optional& , optional& ) BOOST_NOEXCEPT; #endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 4834734..ff8092d 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -39,6 +39,7 @@ run optional_test_assign.cpp ; run optional_test_swap.cpp ; compile optional_test_constexpr.cpp ; compile optional_test_wuninitialized.cpp ; +compile optional_test_fwd_header.cpp ; run optional_test_conversions_from_U.cpp ; run optional_test_convert_from_T.cpp ; run optional_test_convert_assign.cpp ; @@ -48,6 +49,7 @@ run optional_test_flat_map.cpp ; run optional_test_hash.cpp ; run optional_test_map.cpp ; run optional_test_tie.cpp : : : /boost/tuple//boost_tuple ; +run optional_test_ranges_find.cpp ; run optional_test_ref_assign_portable_minimum.cpp ; run optional_test_ref_assign_mutable_int.cpp ; run optional_test_ref_assign_const_int.cpp ; diff --git a/test/optional_test_adl_for_get.cpp b/test/optional_test_adl_for_get.cpp new file mode 100644 index 0000000..431773c --- /dev/null +++ b/test/optional_test_adl_for_get.cpp @@ -0,0 +1,32 @@ +// Copyright (C) 2021 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 +// 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 + +template +struct wrapper {}; + +template +int get(wrapper const&) { return 0; } + +#include "boost/optional/optional.hpp" + +namespace boost +{ + struct any_type_in_boost_namespace {}; +} + +class tag; // user-defined tag + +int main() +{ + // the following tests if boost::get for optional<> does + // not interfere with the global get + return get(wrapper()); +} diff --git a/test/optional_test_constexpr.cpp b/test/optional_test_constexpr.cpp index c521fb8..ffaec72 100644 --- a/test/optional_test_constexpr.cpp +++ b/test/optional_test_constexpr.cpp @@ -11,7 +11,7 @@ #include "boost/optional.hpp" -#ifdef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#ifdef BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION struct Record { @@ -20,43 +20,55 @@ struct Record constexpr int operator()() const { return i; } }; +struct Guard +{ + int i; + constexpr explicit Guard(int i) : i(i) {} + Guard(Guard&&) = delete; +}; + +static_assert(boost::none == boost::none, ""); +static_assert(!(boost::none != boost::none), ""); + 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); + 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 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, ""); + +#ifdef BOOST_OPTIONAL_CONSTEXPR_COPY 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); + static_assert(oNc == oN, ""); + static_assert(oNd == oN, ""); + static_assert(oNe == oN, ""); constexpr bool test_reset() { boost::optional o = 1; @@ -66,31 +78,55 @@ namespace test_int return o == boost::none; } - static_assert(test_reset()); + static_assert(test_reset(), ""); +#endif } namespace test_record { - constexpr boost::optional rN = boost::none; - constexpr boost::optional r1 = Record(1); + 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); + 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_guard +{ + constexpr boost::optional g1 {boost::in_place_init, 1}; + constexpr boost::optional gNa {boost::none}; + constexpr boost::optional gNb; + + static_assert(g1, ""); + static_assert(!!g1, ""); + static_assert(g1 != boost::none, ""); + static_assert(!(g1 == boost::none), ""); + static_assert(g1.has_value(), ""); + + static_assert(!gNa, ""); + static_assert(!gNa.has_value(), ""); + static_assert(gNa == boost::none, ""); + static_assert(!(gNa != boost::none), ""); + + static_assert(!gNb, ""); + static_assert(!gNb.has_value(), ""); + static_assert(gNb == boost::none, ""); + static_assert(!(gNb != boost::none), ""); } namespace test_optional_ref { constexpr int gi = 9; constexpr boost::optional iref = gi; - static_assert(iref); - static_assert(*iref == 9); + static_assert(iref, ""); + static_assert(*iref == 9, ""); } -#endif // BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#endif // BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION diff --git a/test/optional_test_constructible_from_other.cpp b/test/optional_test_constructible_from_other.cpp index af85686..9437b42 100644 --- a/test/optional_test_constructible_from_other.cpp +++ b/test/optional_test_constructible_from_other.cpp @@ -55,5 +55,6 @@ int main() { // Invokes boost::optional copy constructor. Should not invoke wrapper constructor from U. boost::optional< wrapper< int > > res = foo(); + (void)res; return 0; } diff --git a/test/optional_test_fail_explicit_convert_in_value_or.cpp b/test/optional_test_fail_explicit_convert_in_value_or.cpp index 7b6f29c..baa9670 100644 --- a/test/optional_test_fail_explicit_convert_in_value_or.cpp +++ b/test/optional_test_fail_explicit_convert_in_value_or.cpp @@ -29,4 +29,3 @@ void test_implicit_conversion_to_bool() boost::optional opt; opt.value_or(U()); } - diff --git a/test/optional_test_fwd_header.cpp b/test/optional_test_fwd_header.cpp new file mode 100644 index 0000000..8b21455 --- /dev/null +++ b/test/optional_test_fwd_header.cpp @@ -0,0 +1,23 @@ +// Copyright (C) 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 +// 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/optional_fwd.hpp" +#include "boost/optional/optional.hpp" + +void statically_test_basic_instantiations() +{ + boost::optional oN, o1(1); + swap(oN, o1); + + int i = 1; + boost::optional rN, ri(i); + swap(rN, ri); +} diff --git a/test/optional_test_ranges_find.cpp b/test/optional_test_ranges_find.cpp new file mode 100644 index 0000000..8585665 --- /dev/null +++ b/test/optional_test_ranges_find.cpp @@ -0,0 +1,70 @@ +// Copyright (C) 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 +// 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/optional.hpp" +#include "boost/core/lightweight_test.hpp" +#include + +#ifndef BOOST_NO_CXX20_HDR_RANGES +#include +#include +#include +#include +#include + +static_assert(std::equality_comparable, "boost::none shall be equality comparable"); + + +template +void test_that_you_can_find_none_in_a_range_of_optional(T x, T y) +{ + static_assert(std::equality_comparable_with, boost::none_t>, "boost::none shall satisfy the concept"); + static_assert(std::equality_comparable_with >, "boost::none shall satisfy the concept"); + static_assert(std::indirect_binary_predicate*, const boost::none_t*>, "boost::none shall satisfy the concept"); + + // [0] [1] [2] + std::array, 3> arr = {{ x, boost::none, y }}; + auto it = std::ranges::find(arr, boost::none); + BOOST_TEST_EQ(std::distance(arr.begin(), it), 1); +} +#endif // BOOST_NO_CXX20_HDR_RANGES + + +# if (defined __GNUC__) && (!defined BOOST_INTEL_CXX_VERSION) && (!defined __clang__) +# if (__GNUC__ <= 4) +# define BOOST_OPTIONAL_TEST_INSUFFICIENT_TYPE_TRAIT_SUPPORT +# endif +# endif + +#ifndef BOOST_OPTIONAL_TEST_INSUFFICIENT_TYPE_TRAIT_SUPPORT +static_assert(std::is_trivially_copyable::value, "boost::none shall be trivially copyable"); +#endif + + +void test_that_none_is_equal_to_none() +{ + BOOST_TEST(boost::none == boost::none); + BOOST_TEST(!(boost::none != boost::none)); + auto None = boost::none; + BOOST_TEST(None == boost::none); + BOOST_TEST(!(None != boost::none)); +} + +int main() +{ +#ifndef BOOST_NO_CXX20_HDR_RANGES + test_that_you_can_find_none_in_a_range_of_optional(1, 2); + test_that_you_can_find_none_in_a_range_of_optional("one", "two"); +#endif + test_that_none_is_equal_to_none(); + return boost::report_errors(); +} diff --git a/test/optional_test_static_properties.cpp b/test/optional_test_static_properties.cpp index 6c089c6..0921d8f 100644 --- a/test/optional_test_static_properties.cpp +++ b/test/optional_test_static_properties.cpp @@ -20,7 +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_USES_UNION_IMPLEMENTATION #ifndef BOOST_OPTIONAL_DETAIL_NO_DEFAULTED_MOVE_FUNCTIONS struct PrivDefault @@ -136,11 +136,11 @@ void test_trivial_copyability() } #endif -#endif // BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#endif // BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION int main() { -#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#ifndef BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION #ifndef BOOST_OPTIONAL_DETAIL_NO_DEFAULTED_MOVE_FUNCTIONS test_type_traits(); test_trivial_copyability(); diff --git a/test/optional_test_swap.cpp b/test/optional_test_swap.cpp index 8d7b57b..02ba5bf 100644 --- a/test/optional_test_swap.cpp +++ b/test/optional_test_swap.cpp @@ -210,7 +210,7 @@ namespace boost { // Compile time tweaking on whether or not swap should use the default constructor: // -#ifndef BOOST_OPTIONAL_USES_CONSTEXPR_IMPLEMENTATION +#ifndef BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION template <> struct optional_swap_should_use_default_constructor< optional_swap_test::class_whose_default_ctor_should_be_used> : true_type {} ; @@ -219,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 +#endif // BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION // // Specialization of boost::swap: @@ -353,14 +353,14 @@ 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 +#ifndef BOOST_OPTIONAL_USES_UNION_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::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 +#ifndef BOOST_OPTIONAL_USES_UNION_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) ) );