From cbfb66379bd2b5ef5fc03cfb5ef370793a47dfe6 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Fri, 13 Sep 2024 16:22:40 +0200 Subject: [PATCH 01/23] Tags in_place_init and in_place_init_if become inline constexpr --- doc/27_ref_optional_synopsis.qbk | 4 ++-- doc/91_relnotes.qbk | 1 + include/boost/none.hpp | 2 +- include/boost/optional/optional.hpp | 8 ++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/27_ref_optional_synopsis.qbk b/doc/27_ref_optional_synopsis.qbk index d86967bd..d0df811c 100644 --- a/doc/27_ref_optional_synopsis.qbk +++ b/doc/27_ref_optional_synopsis.qbk @@ -16,10 +16,10 @@ namespace boost { class in_place_init_t { /* see below */ } ; ``[link reference_in_place_init __GO_TO__]`` - 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*/ } ; ``[link reference_in_place_init_if __GO_TO__]`` - 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*/ ) ; template class optional ; ``[link reference_operator_template __GO_TO__]`` diff --git a/doc/91_relnotes.qbk b/doc/91_relnotes.qbk index 8b1a9e84..ed16e736 100644 --- a/doc/91_relnotes.qbk +++ b/doc/91_relnotes.qbk @@ -17,6 +17,7 @@ * Dropped dependency on Boost.Utility. * Dropped dependency on Boost.Predef. * A bit faster implementation of some relational operations. +* 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]. [heading Boost Release 1.85] diff --git a/include/boost/none.hpp b/include/boost/none.hpp index 5f927cc3..3e7a9500 100644 --- a/include/boost/none.hpp +++ b/include/boost/none.hpp @@ -50,7 +50,7 @@ namespace { #else -BOOST_INLINE_VARIABLE BOOST_CONSTEXPR_OR_CONST none_t none ((none_t::init_tag())); +BOOST_INLINE_CONSTEXPR none_t none ((none_t::init_tag())); #endif // older definitions diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 6be45355..df3c8a38 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -103,17 +103,17 @@ namespace optional_ns { struct in_place_init_t { struct init_tag{}; - explicit in_place_init_t(init_tag){} + BOOST_CONSTEXPR explicit in_place_init_t(init_tag){} }; -const in_place_init_t in_place_init ((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{}; - explicit in_place_init_if_t(init_tag){} + BOOST_CONSTEXPR explicit in_place_init_if_t(init_tag){} }; -const in_place_init_if_t in_place_init_if ((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 From 07b689fc3e324f0eae12eff51741f6b590c94796 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sat, 14 Sep 2024 01:08:05 +0200 Subject: [PATCH 02/23] Document differences form std --- doc/00_optional.qbk | 5 ++- doc/91_comparison_with_std.qbk | 44 +++++++++++++++++++ doc/{91_relnotes.qbk => 92_relnotes.qbk} | 0 ...owledgments.qbk => 93_acknowledgments.qbk} | 0 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 doc/91_comparison_with_std.qbk rename doc/{91_relnotes.qbk => 92_relnotes.qbk} (100%) rename doc/{92_acknowledgments.qbk => 93_acknowledgments.qbk} (100%) diff --git a/doc/00_optional.qbk b/doc/00_optional.qbk index 093df84f..2fa23576 100644 --- a/doc/00_optional.qbk +++ b/doc/00_optional.qbk @@ -101,5 +101,6 @@ This is how you solve it with `boost::optional`: [include 29_ref_optional_convenience.qbk] [endsect] [include 90_dependencies.qbk] -[include 91_relnotes.qbk] -[include 92_acknowledgments.qbk] +[include 91_comparison_with_std.qbk] +[include 92_relnotes.qbk] +[include 93_acknowledgments.qbk] diff --git a/doc/91_comparison_with_std.qbk b/doc/91_comparison_with_std.qbk new file mode 100644 index 00000000..5d631d6c --- /dev/null +++ b/doc/91_comparison_with_std.qbk @@ -0,0 +1,44 @@ +[/ + Boost.Optional + + Copyright (c) 2015 - 2024 Andrzej Krzemieński + + Distributed under 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) +] + + +[section:std_comp Comparison with `std::optional`] + +[table + [] + [ [[*`boost::opitonal`]] [[*`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.] ] + [ [] [`optional> o {in_place, {1, 2, 3}};`] [No in-place initialization with initializer-list in `boost`.] ] + [ [`optional o {in_place_init_if, cond, a, b};`] [] [No syntax for conditional in-place initialization in `std`.] ] + [ [`optional o {cond, x};`] [] [No syntax for conditional initialization from `T` in `std`.] ] + [ [`optional o {U{}};` + + `optional o {optional{}};`] [`optional o = U{};` + + `optional o = optional{}`] [Constructors form `U` and `optional` are explicit in `boost` and implicit in `std`.] ] + [ [`optional o;`] [] [No optional references in `std`.] ] + [ [] [`constexpr optional o;`] [No `constexpr` interface in `boost`.] ] + [ [`o.map(&f);` + + `o.flat_map(&of);` ] [`o.transform(&f);` + + `o.and_then(&of);`] [Different names and signatures for monadic interface functions. `boost` takes callbacks by value, `std` by universal reference.] ] + [ [] [`o.or_else(&of);`] [No `or_else` function in `boost`.] ] + [ [`o.value_or_eval(&f);`] [] [No `value_or_eval` function in `std`.] ] + [ [] [`optional{} == U{}`; + + `opitonal{} == opitonal{}`] [No comparisons with `U` or `optional` in `boost`.] ] + [ [`make_optional(cond, v);`] [] [No `make_optional` with condition in `std`.] ] + [ [] [`make_optional(a, b);`] [No `make_optional` with specified `T` in `boost`.] ] +] + + +[endsect][/ std_comp] diff --git a/doc/91_relnotes.qbk b/doc/92_relnotes.qbk similarity index 100% rename from doc/91_relnotes.qbk rename to doc/92_relnotes.qbk diff --git a/doc/92_acknowledgments.qbk b/doc/93_acknowledgments.qbk similarity index 100% rename from doc/92_acknowledgments.qbk rename to doc/93_acknowledgments.qbk From cb68f6053d6ae9477d1cc1bec11b28c22ce708d5 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sat, 14 Sep 2024 14:51:54 +0200 Subject: [PATCH 03/23] mention CTAD in std comparison --- doc/91_comparison_with_std.qbk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/91_comparison_with_std.qbk b/doc/91_comparison_with_std.qbk index 5d631d6c..f407ab02 100644 --- a/doc/91_comparison_with_std.qbk +++ b/doc/91_comparison_with_std.qbk @@ -13,7 +13,7 @@ [table [] - [ [[*`boost::opitonal`]] [[*`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.] ] [ [] [`optional> o {in_place, {1, 2, 3}};`] [No in-place initialization with initializer-list in `boost`.] ] @@ -24,6 +24,7 @@ `optional o {optional{}};`] [`optional o = U{};` `optional o = optional{}`] [Constructors form `U` and `optional` are explicit in `boost` and implicit in `std`.] ] + [ [] [`optional o = 1;`] [No clever deduction of of `optional`'s template parameters in initialization in `boost`. ]] [ [`optional o;`] [] [No optional references in `std`.] ] [ [] [`constexpr optional o;`] [No `constexpr` interface in `boost`.] ] [ [`o.map(&f);` @@ -35,7 +36,7 @@ [ [`o.value_or_eval(&f);`] [] [No `value_or_eval` function in `std`.] ] [ [] [`optional{} == U{}`; - `opitonal{} == opitonal{}`] [No comparisons with `U` or `optional` in `boost`.] ] + `optional{} == optional{}`] [No comparisons with `U` or `optional` in `boost`.] ] [ [`make_optional(cond, v);`] [] [No `make_optional` with condition in `std`.] ] [ [] [`make_optional(a, b);`] [No `make_optional` with specified `T` in `boost`.] ] ] From f97e43ce13fa1dd8d145b7e242ae31a2e03d80f4 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sun, 15 Sep 2024 21:12:34 +0200 Subject: [PATCH 04/23] update docs for comparison with std --- doc/91_comparison_with_std.qbk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/91_comparison_with_std.qbk b/doc/91_comparison_with_std.qbk index f407ab02..e72dd46f 100644 --- a/doc/91_comparison_with_std.qbk +++ b/doc/91_comparison_with_std.qbk @@ -16,7 +16,9 @@ [ [[*`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.] ] - [ [] [`optional> o {in_place, {1, 2, 3}};`] [No in-place initialization with initializer-list in `boost`.] ] + [ [] [`optional> o {in_place, {1, 2, 3}};` + + `o.emplace({4, 5, 6});`] [No in-place initialization with initializer-list in `boost`.] ] [ [`optional o {in_place_init_if, cond, a, b};`] [] [No syntax for conditional in-place initialization in `std`.] ] [ [`optional o {cond, x};`] [] [No syntax for conditional initialization from `T` in `std`.] ] [ [`optional o {U{}};` @@ -39,6 +41,7 @@ `optional{} == optional{}`] [No comparisons with `U` or `optional` in `boost`.] ] [ [`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`.]] ] From 1825c778eda2b859d17d1e49c5b0003c36e0da0a Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Wed, 18 Sep 2024 09:28:55 +0200 Subject: [PATCH 05/23] Improve docs Restructure. Rewrite old content. Add new page, on convenience functions --- doc/00_optional.qbk | 21 +- doc/01_quick_start.qbk | 81 +++-- doc/10_motivation.qbk | 80 ----- doc/11_development.qbk | 337 ++++++------------ ...rators.qbk => 12_relational_operators.qbk} | 0 doc/13_convenience.qbk | 102 ++++++ doc/14_monadic_interface.qbk | 45 --- doc/16_optional_references.qbk | 13 +- doc/1A_type_requirements.qbk | 8 +- doc/28_ref_optional_semantics.qbk | 6 +- ...{12_when_to_use.qbk => 31_when_to_use.qbk} | 0 ..._performance.qbk => 32_on_performance.qbk} | 0 doc/90_dependencies.qbk | 2 +- 13 files changed, 299 insertions(+), 396 deletions(-) delete mode 100644 doc/10_motivation.qbk rename doc/{13_relational_operators.qbk => 12_relational_operators.qbk} (100%) create mode 100644 doc/13_convenience.qbk delete mode 100644 doc/14_monadic_interface.qbk rename doc/{12_when_to_use.qbk => 31_when_to_use.qbk} (100%) rename doc/{1B_on_performance.qbk => 32_on_performance.qbk} (100%) diff --git a/doc/00_optional.qbk b/doc/00_optional.qbk index 2fa23576..4809c285 100644 --- a/doc/00_optional.qbk +++ b/doc/00_optional.qbk @@ -49,9 +49,15 @@ Distributed under the Boost Software License, Version 1.0. [def __SPACE__ [$images/space.png]] [def __GO_TO__ [$images/R.png]] +[/ Common terms ] + +[def __UB__ [@https://en.cppreference.com/w/cpp/language/ub ['undefined behavior]]] + [section Introduction] -Class template `optional` is a wrapper for representing 'optional' (or 'nullable') objects who may not (yet) contain a valid value. Optional objects offer full value semantics; they are good for passing by value and usage inside STL containers. This is a header-only library. +Class template `optional` is a wrapper for representing 'optional' (or 'nullable') +objects who may not (yet) contain a valid value. Optional objects offer full value semantics; +they are good for passing by value and usage inside STL containers. This is a header-only C++11 library. [heading Problem] Suppose we want to read a parameter from a config file which represents some integral value, let's call it `"MaxValue"`. It is possible that this parameter is not specified; such situation is no error. It is valid to not specify the parameter and in that case the program is supposed to behave slightly differently. Also, suppose that any possible value of type `int` is a valid value for `"MaxValue"`, so we cannot just use `-1` to represent the absence of the parameter in the config file. @@ -75,19 +81,16 @@ This is how you solve it with `boost::optional`: [endsect] [include 01_quick_start.qbk] -[section Tutorial] -[include 10_motivation.qbk] +[section:design Design Overview and Rationale] [include 11_development.qbk] -[include 12_when_to_use.qbk] -[include 13_relational_operators.qbk] -[include 14_monadic_interface.qbk] +[include 12_relational_operators.qbk] +[include 13_convenience.qbk] [include 15_io.qbk] [include 16_optional_references.qbk] [include 17_in_place_factories.qbk] [include 18_gotchas.qbk] [include 19_exception_safety.qbk] [include 1A_type_requirements.qbk] -[include 1B_on_performance.qbk] [endsect] [section:reference Reference] [include 21_ref_none.qbk] @@ -100,6 +103,10 @@ This is how you solve it with `boost::optional`: [endsect] [include 29_ref_optional_convenience.qbk] [endsect] +[section:advice Advice] +[include 31_when_to_use.qbk] +[include 32_on_performance.qbk] +[endsect] [include 90_dependencies.qbk] [include 91_comparison_with_std.qbk] [include 92_relnotes.qbk] diff --git a/doc/01_quick_start.qbk b/doc/01_quick_start.qbk index 223603dd..9d2c35de 100644 --- a/doc/01_quick_start.qbk +++ b/doc/01_quick_start.qbk @@ -10,16 +10,24 @@ ] -[section Quick Start] +[section Quick Overview] [section Optional return values] -Let's write and use a converter function that converts a `std::string` to an `int`. It is possible that for a given string (e.g. `"cat"`) there exists no value of type `int` capable of representing the conversion result. We do not consider such situation an error. We expect that the converter can be used only to check if the conversion is possible. A natural signature for this function can be: +Let's write and use a converter function that converts a `std::string` to an `int`. +It is possible that for a given string (e.g. `"cat"`) there exists no value of type +`int` capable of representing the conversion result. We do not consider such +situation an error. We expect that the converter can be used only to check if +the conversion is possible. A natural signature for this function can be: #include boost::optional convert(const std::string& text); -All necessary functionality can be included with one header ``. The above function signature means that the function can either return a value of type `int` or a flag indicating that no value of `int` is available. This does not indicate an error. It is like one additional value of `int`. This is how we can use our function: +All necessary functionality can be included with one header ``. +The above function signature means that the function can either return a value +of type `int` or a flag indicating that no value of `int` is available. +This does not indicate an error. It is like one additional value of `int`. +This is how we can use our function: const std::string& text = /*... */; boost::optional oi = convert(text); // move-construct @@ -116,29 +124,10 @@ 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.tutorial.in_place_factories In-Place Factories].] +[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] -[section Bypassing unnecessary default construction] - -Suppose we have class `Date`, which does not have a default constructor: there is no good candidate for a default date. We have a function that returns two dates in form of a `boost::tuple`: - - boost::tuple getPeriod(); - -In other place we want to use the result of `getPeriod`, but want the two dates to be named: `begin` and `end`. We want to implement something like 'multiple return values': - - Date begin, end; // Error: no default ctor! - boost::tie(begin, end) = getPeriod(); - -The second line works already, this is the capability of __BOOST_TUPLE__ library, but the first line won't work. We could set some invented initial dates, but it is confusing and may be an unacceptable cost, given that these values will be overwritten in the next line anyway. This is where `optional` can help: - - boost::optional begin, end; - boost::tie(begin, end) = getPeriod(); - -It works because inside `boost::tie` a move-assignment from `T` is invoked on `optional`, which internally calls a move-constructor of `T`. -[endsect] - [section Storage in containers] Suppose you want to ask users to choose some number (an `int`). One of the valid responses is to choose nothing, which is represented by an uninitialized `optional`. You want to make a histogram showing how many times each choice was made. You can use an `std::map`: @@ -157,4 +146,50 @@ which is compared less than any value of `T`. as it provides specializations for `std::hash`. [endsect] +[section Monadic interface] + +The monadic interface of `optional` allows the application of functions +to optional values without resorting to the usage of explicit `if`-statements. + +Function `map` takes a function mapping type `T` onto type `U` and maps an `optional` +onto an `optional` using the provided function. + + int length(const string& s){ return s.size(); }; + + optional null{}, thin{""}, word{"word"}; + assert (null.map(length) == none); + assert (thin.map(length) == 0); + assert (word.map(length) == 4); + +Function `flat_map` is similar, but it requires the function to return an +`optional` for some type `V`. This `optional` becomes the return type of +`flat_map`. + + optional first_char(const string& s) { + if (s.empty()) return none; + else return s[0]; + }; + + optional null{}, thin{""}, word{"word"}; + assert (null.flat_map(first_char) == none); + assert (thin.flat_map(first_char) == none); + assert (word.flat_map(first_char) == 'w'); + +These functions can be combined in one expression reflecting a chain of computations: + + auto get_contents(path p) -> optional; + auto trim(string) -> string; + auto length(string) -> int; + + auto trimmed_size_of(optional p) -> int + { + return p.flat_map(get_contents) + .map(trim) + .map(length) + .value_or(0); + } + + +[endsect] + [endsect] diff --git a/doc/10_motivation.qbk b/doc/10_motivation.qbk deleted file mode 100644 index fe5b6dac..00000000 --- a/doc/10_motivation.qbk +++ /dev/null @@ -1,80 +0,0 @@ -[/ - Boost.Optional - - Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal - Copyright (c) 2014 Andrzej Krzemienski - - Distributed under 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) -] - -[section Motivation] - -Consider these functions which should return a value but which might not have -a value to return: - -* (A) `double sqrt(double n );` -* (B) `char get_async_input();` -* (C) `point polygon::get_any_point_effectively_inside();` - -There are different approaches to the issue of not having a value to return. - -A typical approach is to consider the existence of a valid return value as a -postcondition, so that if the function cannot compute the value to return, it -has either undefined behavior (and can use assert in a debug build) or uses a -runtime check and throws an exception if the postcondition is violated. This -is a reasonable choice for example, for function (A), because the lack of a -proper return value is directly related to an invalid parameter (out of domain -argument), so it is appropriate to require the callee to supply only parameters -in a valid domain for execution to continue normally. - -However, function (B), because of its asynchronous nature, does not fail just -because it can't find a value to return; so it is incorrect to consider such -a situation an error and assert or throw an exception. This function must -return, and somehow, must tell the callee that it is not returning a meaningful -value. - -A similar situation occurs with function (C): it is conceptually an error to -ask a ['null-area] polygon to return a point inside itself, but in many -applications, it is just impractical for performance reasons to treat this as -an error (because detecting that the polygon has no area might be too expensive -to be required to be tested previously), and either an arbitrary point -(typically at infinity) is returned, or some efficient way to tell the callee -that there is no such point is used. - -There are various mechanisms to let functions communicate that the returned -value is not valid. One such mechanism, which is quite common since it has -zero or negligible overhead, is to use a special value which is reserved to -communicate this. Classical examples of such special values are `EOF`, -`string::npos`, points at infinity, etc... - -When those values exist, i.e. the return type can hold all meaningful values -['plus] the ['signal] value, this mechanism is quite appropriate and well known. -Unfortunately, there are cases when such values do not exist. In these cases, -the usual alternative is either to use a wider type, such as `int` in place of -`char`; or a compound type, such as `std::pair`. - -Returning a `std::pair`, thus attaching a boolean flag to the result -which indicates if the result is meaningful, has the advantage that can be -turned into a consistent idiom since the first element of the pair can be -whatever the function would conceptually return. For example, the last two -functions could have the following interface: - - std::pair get_async_input(); - std::pair polygon::get_any_point_effectively_inside(); - -These functions use a consistent interface for dealing with possibly nonexistent -results: - - std::pair p = poly.get_any_point_effectively_inside(); - if ( p.second ) - flood_fill(p.first); - -However, not only is this quite a burden syntactically, it is also error prone -since the user can easily use the function result (first element of the pair) -without ever checking if it has a valid value. - -Clearly, we need a better idiom. - -[endsect] diff --git a/doc/11_development.qbk b/doc/11_development.qbk index de5d00d7..e5ef1ed3 100644 --- a/doc/11_development.qbk +++ b/doc/11_development.qbk @@ -2,250 +2,129 @@ Boost.Optional Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal + Copyright (c) 2024 andrzej Krzemieński Distributed under 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) ] -[section Design Overview] - -[section The models] - -In C++, we can ['declare] an object (a variable) of type `T`, and we can give this -variable an ['initial value] (through an ['initializer]. (cf. 8.5)). -When a declaration includes a non-empty initializer (an initial value is given), -it is said that the object has been initialized. -If the declaration uses an empty initializer (no initial value is given), and -neither default nor value initialization applies, it is said that the object is -[*uninitialized]. Its actual value exist but has an ['indeterminate initial value] -(cf. 8.5/11). -`optional` intends to formalize the notion of initialization (or lack of it) -allowing a program to test whether an object has been initialized and stating -that access to the value of an uninitialized object is undefined behavior. That -is, when a variable is declared as `optional` and no initial value is given, -the variable is ['formally] uninitialized. A formally uninitialized optional object -has conceptually no value at all and this situation can be tested at runtime. It -is formally ['undefined behavior] to try to access the value of an uninitialized -optional. An uninitialized optional can be assigned a value, in which case its initialization state changes to initialized. Furthermore, given the formal -treatment of initialization states in optional objects, it is even possible to -reset an optional to ['uninitialized]. - -In C++ there is no formal notion of uninitialized objects, which means that -objects always have an initial value even if indeterminate. -As discussed on the previous section, this has a drawback because you need -additional information to tell if an object has been effectively initialized. -One of the typical ways in which this has been historically dealt with is via -a special value: `EOF`, `npos`, -1, etc... This is equivalent to adding the -special value to the set of possible values of a given type. This super set of -`T` plus some ['nil_t]—where `nil_t` is some stateless POD—can be modeled in modern -languages as a [*discriminated union] of T and nil_t. Discriminated unions are -often called ['variants]. A variant has a ['current type], which in our case is either -`T` or `nil_t`. -Using the __BOOST_VARIANT__ library, this model can be implemented in terms of `boost::variant`. -There is precedent for a discriminated union as a model for an optional value: -the __HASKELL__ [*Maybe] built-in type constructor. Thus, a discriminated union -`T+nil_t` serves as a conceptual foundation. - -A `variant` follows naturally from the traditional idiom of extending -the range of possible values adding an additional sentinel value with the -special meaning of ['Nothing]. However, this additional ['Nothing] value is largely -irrelevant for our purpose since our goal is to formalize the notion of -uninitialized objects and, while a special extended value can be used to convey -that meaning, it is not strictly necessary in order to do so. - -The observation made in the last paragraph about the irrelevant nature of the -additional `nil_t` with respect to [_purpose] of `optional` suggests an -alternative model: a ['container] that either has a value of `T` or nothing. - -As of this writing I don't know of any precedent for a variable-size -fixed-capacity (of 1) stack-based container model for optional values, yet I -believe this is the consequence of the lack of practical implementations of -such a container rather than an inherent shortcoming of the container model. - -In any event, both the discriminated-union or the single-element container -models serve as a conceptual ground for a class representing optional—i.e. -possibly uninitialized—objects. -For instance, these models show the ['exact] semantics required for a wrapper -of optional values: - -Discriminated-union: - -* [*deep-copy] semantics: copies of the variant implies copies of the value. -* [*deep-relational] semantics: comparisons between variants matches both -current types and values -* If the variant's current type is `T`, it is modeling an ['initialized] optional. -* If the variant's current type is not `T`, it is modeling an ['uninitialized] -optional. -* Testing if the variant's current type is `T` models testing if the optional -is initialized -* Trying to extract a `T` from a variant when its current type is not `T`, models -the undefined behavior of trying to access the value of an uninitialized optional - -Single-element container: - -* [*deep-copy] semantics: copies of the container implies copies of the value. -* [*deep-relational] semantics: comparisons between containers compare container -size and if match, contained value -* If the container is not empty (contains an object of type `T`), it is modeling -an ['initialized] optional. -* If the container is empty, it is modeling an ['uninitialized] optional. -* Testing if the container is empty models testing if the optional is -initialized -* Trying to extract a `T` from an empty container models the undefined behavior -of trying to access the value of an uninitialized optional +[section Design Goals] + +In C++ you can create an automatic object of a scalar type, and manipulate it, +without assigning it the initial value. + + { + int i; // indeterminate value + populate(&i); + cout << i; + } + +Such an object is said to have ['indeterminate value]. If you subsequently +assign a proper value to the object, all is fine; but if the program tries to +read an indeterminate value, this is ['undefined behavior'], and since C++26 +this is ['erroneous behavior]. +In any case, the program is now likely to do something else than what the +programmer intended. In case you have some object `i`, and you do not know if it +has an indeterminate value, or a normal, intended, value, there is no way to +check it, because the checking would require reading the value. + +This is one of the primary problems that `optional` was intended to address: so +that you may have a type that knows whether it has been assigned a proper value, +and it can tell you that if requested. + +In the case of type `int` the internal representation of such a class could be: + + class OptionalInt + { + bool _has_value = false; + int _value; + }; + +In the general case, the internal representation is something equivalent to: + + template + class Optional + { + bool _has_value = false; + alignas(T) char _value [sizeof(T)]; + }; + +Next, because we need to pass around these "optional" `int`s as normal `int`s, +like returning them from functions, when copying, we need to copy `_has_value`, +which indicates whether we have the value or not, and, if we do have value, and +only then, to also copy `_value`. + +This means that our type requires ['deep copy] semantics. + +[note +This is a C++ equivalent of a +[@https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Maybe.html Maybe] +monad in [@http://www.haskell.org/ Haskell]. +] [endsect] -[section The semantics] - -Objects of type `optional` are intended to be used in places where objects of -type `T` would but which might be uninitialized. Hence, `optional`'s purpose is -to formalize the additional possibly uninitialized state. -From the perspective of this role, `optional` can have the same operational -semantics of `T` plus the additional semantics corresponding to this special -state. -As such, `optional` could be thought of as a ['supertype] of `T`. Of course, we -can't do that in C++, so we need to compose the desired semantics using a -different mechanism. -Doing it the other way around, that is, making `optional` a ['subtype] of `T` -is not only conceptually wrong but also impractical: it is not allowed to -derive from a non-class type, such as a built-in type. - -We can draw from the purpose of `optional` the required basic semantics: - -* [*Default Construction:] To introduce a formally uninitialized wrapped -object. -* [*Direct Value Construction via copy:] To introduce a formally initialized -wrapped object whose value is obtained as a copy of some object. -* [*Deep Copy Construction:] To obtain a new yet equivalent wrapped object. -* [*Direct Value Assignment (upon initialized):] To assign a value to the -wrapped object. -* [*Direct Value Assignment (upon uninitialized):] To initialize the wrapped -object with a value obtained as a copy of some object. -* [*Assignment (upon initialized):] To assign to the wrapped object the value -of another wrapped object. -* [*Assignment (upon uninitialized):] To initialize the wrapped object with -value of another wrapped object. -* [*Deep Relational Operations (when supported by the type T):] To compare -wrapped object values taking into account the presence of uninitialized states. -* [*Value access:] To unwrap the wrapped object. -* [*Initialization state query:] To determine if the object is formally -initialized or not. -* [*Swap:] To exchange wrapped objects. (with whatever exception safety -guarantees are provided by `T`'s swap). -* [*De-initialization:] To release the wrapped object (if any) and leave the -wrapper in the uninitialized state. - -Additional operations are useful, such as converting constructors and -converting assignments, in-place construction and assignment, and safe -value access via a pointer to the wrapped object or null. +[section:iface Interface Design] +One part of the interface is for modifying and setting the initial state of +the object. It has to be able to say that -[endsect] +* we want to store a specific value of type `T`, +* we want to store no value. + +The default constructor stores no value. Other than that, we require that +the assignment and construction from a `T` reflects the former, while assignment +and construction of a special ['tag] value `none` reflect the latter need. + + optional o1; // contains no value + optional o2 = 2; // contains value 2 + optional o3 = none; // contains no value + + o1 = 1; // assign value 1 + o2 = none; // assign a no-value + o3 = {}; // assign a no-value + +[heading Inspecting the State] + +Inspecting the state of an optional object requires two steps: -[section The Interface] - -Since the purpose of optional is to allow us to use objects with a formal -uninitialized additional state, the interface could try to follow the -interface of the underlying `T` type as much as possible. In order to choose -the proper degree of adoption of the native `T` interface, the following must -be noted: Even if all the operations supported by an instance of type `T` are -defined for the entire range of values for such a type, an `optional` -extends such a set of values with a new value for which most -(otherwise valid) operations are not defined in terms of `T`. - -Furthermore, since `optional` itself is merely a `T` wrapper (modeling a `T` -supertype), any attempt to define such operations upon uninitialized optionals -will be totally artificial w.r.t. `T`. - -This library chooses an interface which follows from `T`'s interface only for -those operations which are well defined (w.r.t the type `T`) even if any of the -operands are uninitialized. These operations include: construction, -copy-construction, assignment, swap and relational operations. - -For the value access operations, which are undefined (w.r.t the type `T`) when -the operand is uninitialized, a different interface is chosen (which will be -explained next). - -Also, the presence of the possibly uninitialized state requires additional -operations not provided by `T` itself which are supported by a special interface. - -[heading Lexically-hinted Value Access in the presence of possibly -uninitialized optional objects: The operators * and ->] - -A relevant feature of a pointer is that it can have a [*null pointer value]. -This is a ['special] value which is used to indicate that the pointer is not -referring to any object at all. In other words, null pointer values convey -the notion of nonexistent objects. - -This meaning of the null pointer value allowed pointers to became a ['de -facto] standard for handling optional objects because all you have to do -to refer to a value which you don't really have is to use a null pointer -value of the appropriate type. Pointers have been used for decades—from -the days of C APIs to modern C++ libraries—to ['refer] to optional (that is, -possibly nonexistent) objects; particularly as optional arguments to a -function, but also quite often as optional data members. - -The possible presence of a null pointer value makes the operations that -access the pointee's value possibly undefined, therefore, expressions which -use dereference and access operators, such as: `( *p = 2 )` and `( p->foo() )`, -implicitly convey the notion of optionality, and this information is tied to -the ['syntax] of the expressions. That is, the presence of operators `*` and `->` -tell by themselves —without any additional context— that the expression will -be undefined unless the implied pointee actually exist. - -Such a ['de facto] idiom for referring to optional objects can be formalized -in the form of a concept: the __OPTIONAL_POINTEE__ concept. -This concept captures the syntactic usage of operators `*`, `->` and -contextual conversion to `bool` to convey the notion of optionality. - -However, pointers are good to [_refer] to optional objects, but not particularly -good to handle the optional objects in all other respects, such as initializing -or moving/copying them. The problem resides in the shallow-copy of pointer -semantics: if you need to effectively move or copy the object, pointers alone -are not enough. The problem is that copies of pointers do not imply copies of -pointees. For example, as was discussed in the motivation, pointers alone -cannot be used to return optional objects from a function because the object -must move outside from the function and into the caller's context. - -A solution to the shallow-copy problem that is often used is to resort to -dynamic allocation and use a smart pointer to automatically handle the details -of this. For example, if a function is to optionally return an object `X`, it can -use `shared_ptr` as the return value. However, this requires dynamic allocation -of `X`. If `X` is a built-in or small POD, this technique is very poor in terms of -required resources. Optional objects are essentially values so it is very -convenient to be able to use automatic storage and deep-copy semantics to -manipulate optional values just as we do with ordinary values. Pointers do -not have this semantics, so are inappropriate for the initialization and -transport of optional values, yet are quite convenient for handling the access -to the possible undefined value because of the idiomatic aid present in the -__OPTIONAL_POINTEE__ concept incarnated by pointers. - - -[heading Optional as a model of OptionalPointee] - -For value access operations `optional<>` uses operators `*` and `->` to -lexically warn about the possibly uninitialized state appealing to the -familiar pointer semantics w.r.t. to null pointers. +* check if we have the value or not, +* if so, read the stored value. + +This 'procedure' is characteristic of inspecting pointers in C++, therefore the +pointer-like syntax was chosen to represent this. + + void inspect (optional os) + { + if (os) { // contextual conversion to `bool` + read_string(*os); // `operator*` to access the stored value + read_int(os->size()); // `operator->` as shortcut for accessing members + } + } + +Also, similarly to pointers, if you access the value when it is not there, +you trigger __UB__. +This library detects and reports it via +[@../../../assert/assert.html `BOOST_ASSERT()`]. This common property of +pointers and `optional<>` has been formalized into a concept __OPTIONAL_POINTEE__. + +However, there is also the counter-intuitive part. All pointers embed ['shallow-copy] +semantics: when you copy a pointer, the pointed-to object stays at the same location +and you can access it via either of the pointers. This is unlike optional objects +where the represented value is copied along. [caution -However, it is particularly important to note that `optional<>` objects -are not pointers. [_`optional<>` is not, and does not model, a pointer]. +Optional objects are not pointers. ] -For instance, `optional<>` does not have shallow-copy so does not alias: -two different optionals never refer to the ['same] value unless `T` itself is -a reference (but may have ['equivalent] values). -The difference between an `optional` and a pointer must be kept in mind, -particularly because the semantics of relational operators are different: -since `optional` is a value-wrapper, relational operators are deep: they -compare optional values; but relational operators for pointers are shallow: -they do not compare pointee values. -As a result, you might be able to replace `optional` by `T*` on some -situations but not always. Specifically, on generic code written for both, -you cannot use relational operators directly, and must use the template -functions __FUNCTION_EQUAL_POINTEES__ and __FUNCTION_LESS_POINTEES__ instead. +There is a similar difference in relational operations: they compare deeply for +`optional<>`, while they are shallow for pointers. -[endsect] +[note +When you need a deep relational operations that work uniformly for `optional<>` +and pointers in generic contexts, use functions +[@../../../utility/OptionalPointee.html#equal `equal_pointees()`] and +[@../../../utility/OptionalPointee.html#less `less_pointees()`]. +] [endsect] diff --git a/doc/13_relational_operators.qbk b/doc/12_relational_operators.qbk similarity index 100% rename from doc/13_relational_operators.qbk rename to doc/12_relational_operators.qbk diff --git a/doc/13_convenience.qbk b/doc/13_convenience.qbk new file mode 100644 index 00000000..9a95de3a --- /dev/null +++ b/doc/13_convenience.qbk @@ -0,0 +1,102 @@ + +[section Convenience Conversions and Deductions] + +Unlike `std::optional`, `boost::optional` does not offer a number of +"convenience" converting constructors, mixed relational operations and +deductions for class template parameters. + + std::optional oi = 1; // OK + + std:string_view sv = "hi"; + std::optional os = sv; // OK + os == sv; // OK + + std::optional osv; + std::optional os2 = osv; // OK + os2 == osv; // OK + +They are practical, ans sometimes stem from the argument for consistency: +if `(optT && *optT == u)` works then `(optT == u)` should also work. + +However, these intelligent convenience functions sometimes produce results +that are counter to the programmer intentions and produce silent bugs. + +Consider a more complicated example: + + Threshold th = /*...*/; + std::optional o = th; + assert (o); + +In this code, can we expect that thus initialized `optional` contains a value? +The answer is: it depends on the type of `Threshold`. It can be defined as: + + using Threshold = std::optional; + +And then the assertion will fire. This is because in this case the intelligence +decides that since we already have an optional, the additional wrapping into +a yet another optional is unnecessary. + +If we explicitly specify the template type, the situation doesn't get less +complicated. + + Threshold th; + std::optional o = th; + assert(o); + +Can this assertion fire? Now we have two competing constructors: + + template + optional(U const&); + + template + optional(optional const&); + +Which one will get chosen? Actually, we are lucky, and it is going to be the +first one due to concept tricks. But let's try a different example: + + Threshold th; + std::optional o = th; + assert(o); + assert(o == th); + +Here, the first assertion passes, but the second one fires. This is because +there are two competing overloads of the comparison operator: + + template + bool operator==(optional const&, U const&); + + template + bool operator==(optional const&, optional const&); + +And this time there is no concept trickery, so the second overload is chosen, +and gives different results: we are comparing an optional object `th`, which does +not contain a value, with an optional object `o` which does contain a value. + +This problem -- that the operations compile, but have runtime behavior counter +to programmer's intuition -- gains new significance with the introduction of +concepts to C++. + + static_assert(std::equality_comparable_with, Threshold>); + +Concepts have both syntactic constraints and semantic constraints. Syntactic +constraints are statically checked by the compiler. For semantic constraints, +functions that use the concept trust the programmer that these constraints are +met, and if not, this is __UB__. + +These are problems with `std::optional`. `boost::optional` doesn't have these +problems, because it does not offer the said convenience operations. + +The design principle for `boost::optional` is not to offer functionality that +nicely deduces the programmer intentions in 95% of the cases, and in the remaining +5% renders effects counter to programmer expectations. + +Instead, this library recommends using a more verbose syntax that works in 100% +of the cases: + + Threshold th; + auto o = boost::make_potional(th); // *always* add a new layer of optionality + + return boost::equal_pointees(o, th); // *always* unpack optionals for comparison + return o && *o == th; // *always* treat the right-hand side argument as value + +[endsect] diff --git a/doc/14_monadic_interface.qbk b/doc/14_monadic_interface.qbk deleted file mode 100644 index 21731e48..00000000 --- a/doc/14_monadic_interface.qbk +++ /dev/null @@ -1,45 +0,0 @@ -[section Monadic interface] - -The monadic interface of `optional` allows the application of functions -to optional values without resorting to the usage of explicit `if`-statements. - -Function `map` takes a function mapping type `T` onto type `U` and maps an `optional` -onto an `optional` using the provided function. - - int length(const string& s){ return s.size(); }; - - optional null{}, thin{""}, word{"word"}; - assert (null.map(length) == none); - assert (thin.map(length) == 0); - assert (word.map(length) == 4); - -Function `flat_map` is similar, but it requires the function to return an -`optional` for some type `V`. This `optional` becomes the return type of -`flat_map`. - - optional first_char(const string& s) { - if (s.empty()) return none; - else return s[0]; - }; - - optional null{}, thin{""}, word{"word"}; - assert (null.flat_map(first_char) == none); - assert (thin.flat_map(first_char) == none); - assert (word.flat_map(first_char) == 'w'); - -These functions can be combined in one expression reflecting a chain of computations: - - auto get_contents(path p) -> optional; - auto trim(string) -> string; - auto length(string) -> int; - - auto trimmed_size_of(optional p) -> int - { - return p.flat_map(get_contents) - .map(trim) - .map(length) - .value_or(0); - } - - -[endsect] diff --git a/doc/16_optional_references.qbk b/doc/16_optional_references.qbk index af663e1b..10664d72 100644 --- a/doc/16_optional_references.qbk +++ b/doc/16_optional_references.qbk @@ -23,7 +23,12 @@ will nonetheless refer to the same object. * Value-access will actually provide access to the referenced object rather than the reference itself. -[caution 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].] +[caution +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]. +] [heading Rvalue references] @@ -45,7 +50,7 @@ the first time) to the object. Clearly, there is no other choice. optional orb(x) ; ora = orb ; // now 'ora' is bound to 'x' through 'rx' *ora = 2 ; // Changes value of 'x' through 'ora' - assert(x==2); + assert(x==2); If you assign to a bare C++ reference, the assignment is forwarded to the referenced object; its value changes but the reference is never rebound. @@ -71,8 +76,8 @@ bare C++ references. optional orb(rb) ; ora = orb ; // 'ora' is rebound to 'b' *ora = 3 ; // Changes value of 'b' (not 'a') - assert(a==1); - assert(b==3); + assert(a==1); + assert(b==3); [heading Rationale] diff --git a/doc/1A_type_requirements.qbk b/doc/1A_type_requirements.qbk index 989cc4ab..214fcbff 100644 --- a/doc/1A_type_requirements.qbk +++ b/doc/1A_type_requirements.qbk @@ -7,8 +7,8 @@ The very minimum requirement of `optional` is that `T` is a complete type and assert(o == none); // check if initialized 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.tutorial.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.design.in_place_factories In-Place Factories]: optional o; o.emplace("T", "ctor", "params"); @@ -17,13 +17,13 @@ If `T` is __MOVE_CONSTRUCTIBLE__, `optional` is also __MOVE_CONSTRUCTIBLE__ a optional o = make_T(); optional p = optional(); - + If `T` is __COPY_CONSTRUCTIBLE__, `optional` is also __COPY_CONSTRUCTIBLE__ and can be easily initialized from an lvalue of type `T`: T v = make_T(); optional o = v; optional p = o; - + If `T` is not `MoveAssignable`, it is still possible to reset the value of `optional` using function `emplace()`: optional o = make_T(); diff --git a/doc/28_ref_optional_semantics.qbk b/doc/28_ref_optional_semantics.qbk index d3cb6eed..0fc90a73 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.tutorial.in_place_factories In-Place Factories] +* [*Notes:] See [link boost_optional.design.in_place_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.tutorial.in_place_factories In-Place Factories] +* [*Notes:] See [link boost_optional.design.in_place_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]. @@ -963,7 +963,7 @@ __SPACE__ * [*Postconditions:] `bool(*this) == bool(rhs)`. -* [*Notes:] This behaviour is called ['rebinding semantics]. See [link boost_optional.tutorial.optional_references.rebinding_semantics_for_assignment_of_optional_references here] for details. +* [*Notes:] This behaviour is called ['rebinding semantics]. See [link boost_optional.design.optional_references.rebinding_semantics_for_assignment_of_optional_references here] for details. * [*Example:] `` diff --git a/doc/12_when_to_use.qbk b/doc/31_when_to_use.qbk similarity index 100% rename from doc/12_when_to_use.qbk rename to doc/31_when_to_use.qbk diff --git a/doc/1B_on_performance.qbk b/doc/32_on_performance.qbk similarity index 100% rename from doc/1B_on_performance.qbk rename to doc/32_on_performance.qbk diff --git a/doc/90_dependencies.qbk b/doc/90_dependencies.qbk index e0beee32..acc23c10 100644 --- a/doc/90_dependencies.qbk +++ b/doc/90_dependencies.qbk @@ -57,7 +57,7 @@ On compilers that do not support rvalue references, each of these functions is s template void emplace(Arg& arg); void emplace(); -This workaround addresses about 40% of all use cases. If this is insufficient, you need to resort to using [link boost_optional.tutorial.in_place_factories In-Place Factories]. +This workaround addresses about 40% of all use cases. If this is insufficient, you need to resort to using [link boost_optional.design.in_place_factories In-Place Factories]. [endsect] [section Optional Reference Binding][#optional_reference_binding] From dc7909db4a6cdfd7636ac593616b0b03b89c21e0 Mon Sep 17 00:00:00 2001 From: typenameTea Date: Wed, 18 Sep 2024 22:08:58 +0100 Subject: [PATCH 06/23] refactor: implementation tidy up C++11 is now the library minimum requirement, so clean up the interface by removing conditional compilation intended to support C++03 alongside C++11. --- .../optional_trivially_copyable_base.hpp | 242 +------- include/boost/optional/optional.hpp | 517 +----------------- 2 files changed, 2 insertions(+), 757 deletions(-) diff --git a/include/boost/optional/detail/optional_trivially_copyable_base.hpp b/include/boost/optional/detail/optional_trivially_copyable_base.hpp index 1d157cfe..a8e0371c 100644 --- a/include/boost/optional/detail/optional_trivially_copyable_base.hpp +++ b/include/boost/optional/detail/optional_trivially_copyable_base.hpp @@ -25,10 +25,8 @@ class tc_optional_base : public optional_tag protected: typedef T & reference_type ; typedef T const& reference_const_type ; -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES typedef T && rval_reference_type ; typedef T && reference_type_of_temporary_wrapper ; -#endif typedef T * pointer_type ; typedef T const* pointer_const_type ; typedef T const& argument_type ; @@ -51,9 +49,6 @@ class tc_optional_base : public optional_tag // tc_optional_base ( tc_optional_base const& ) = default; - -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES - template explicit tc_optional_base ( Expr&& expr, PtrExpr const* tag ) : @@ -62,20 +57,6 @@ class tc_optional_base : public optional_tag construct(boost::forward(expr),tag); } -#else - // This is used for both converting and in-place constructions. - // Derived classes use the 'tag' to select the appropriate - // implementation (the correct 'construct()' overload) - template - explicit tc_optional_base ( Expr const& expr, Expr const* tag ) - : - m_initialized(false) - { - construct(expr,tag); - } - -#endif - // tc_optional_base& operator= ( tc_optional_base const& ) = default; // ~tc_optional_base() = default; @@ -99,7 +80,6 @@ class tc_optional_base : public optional_tag m_initialized = rhs.is_initialized(); } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // move-assigns from another _convertible_ optional (deep-moves from the rhs value) template void assign ( optional&& rhs ) @@ -109,7 +89,6 @@ class tc_optional_base : public optional_tag m_storage = static_cast(rhs.get()); m_initialized = rhs.is_initialized(); } -#endif void assign ( argument_type val ) { @@ -120,19 +99,11 @@ class tc_optional_base : public optional_tag #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES template void assign_expr ( Expr&& expr, ExprPtr const* tag ) { construct(boost::forward(expr),tag); } -#else - template - void assign_expr ( Expr const& expr, Expr const* tag ) - { - construct(expr,tag); - } -#endif #endif @@ -162,7 +133,6 @@ class tc_optional_base : public optional_tag } -#if (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) && (!defined BOOST_NO_CXX11_VARIADIC_TEMPLATES) // Constructs in-place // upon exception *this is always uninitialized template @@ -194,146 +164,9 @@ class tc_optional_base : public optional_tag if ( cond ) construct(in_place_init, boost::forward(args)...); } -#elif (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) - template - void construct ( in_place_init_t, Arg&& arg ) - { - m_storage = value_type( boost::forward(arg) ); - m_initialized = true ; - } - - void construct ( in_place_init_t ) - { - m_storage = value_type(); - m_initialized = true ; - } - - template - void emplace_assign ( Arg&& arg ) - { - construct(in_place_init, boost::forward(arg)) ; - } - - void emplace_assign () - { - construct(in_place_init) ; - } - - template - explicit tc_optional_base ( in_place_init_t, Arg&& arg ) - : - m_initialized(false) - { - construct(in_place_init, boost::forward(arg)); - } - - explicit tc_optional_base ( in_place_init_t ) - : - m_initialized(false), m_storage() {} - - template - explicit tc_optional_base ( in_place_init_if_t, bool cond, Arg&& arg ) - : - m_initialized(false) - { - if ( cond ) - construct(in_place_init, boost::forward(arg)); - } - - explicit tc_optional_base ( in_place_init_if_t, bool cond ) - : - m_initialized(false) - { - if ( cond ) - construct(in_place_init); - } - -#else - - template - void construct ( in_place_init_t, const Arg& arg ) - { - m_storage = value_type( arg ); - m_initialized = true ; - } - - template - void construct ( in_place_init_t, Arg& arg ) - { - m_storage = value_type( arg ); - m_initialized = true ; - } - - void construct ( in_place_init_t ) - { - m_storage = value_type(); - m_initialized = true ; - } - - template - void emplace_assign ( const Arg& arg ) - { - construct(in_place_init, arg); - } - - template - void emplace_assign ( Arg& arg ) - { - construct(in_place_init, arg); - } - - void emplace_assign () - { - construct(in_place_init); - } - - template - explicit tc_optional_base ( in_place_init_t, const Arg& arg ) - : m_initialized(false) - { - construct(in_place_init, arg); - } - - template - explicit tc_optional_base ( in_place_init_t, Arg& arg ) - : m_initialized(false) - { - construct(in_place_init, arg); - } - - explicit tc_optional_base ( in_place_init_t ) - : m_initialized(false) - { - construct(in_place_init); - } - - template - explicit tc_optional_base ( in_place_init_if_t, bool cond, const Arg& arg ) - : m_initialized(false) - { - if ( cond ) - construct(in_place_init, arg); - } - - template - explicit tc_optional_base ( in_place_init_if_t, bool cond, Arg& arg ) - : m_initialized(false) - { - if ( cond ) - construct(in_place_init, arg); - } - - explicit tc_optional_base ( in_place_init_if_t, bool cond ) - : m_initialized(false) - { - if ( cond ) - construct(in_place_init); - } -#endif #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Constructs in-place using the given factory template void construct ( Expr&& factory, in_place_factory_base const* ) @@ -365,42 +198,8 @@ class tc_optional_base : public optional_tag construct(factory,tag); } -#else - // Constructs in-place using the given factory - template - void construct ( Expr const& factory, in_place_factory_base const* ) - { - boost_optional_detail::construct(factory, boost::addressof(m_storage)); - m_initialized = true ; - } - - // Constructs in-place using the given typed factory - template - void construct ( Expr const& factory, typed_in_place_factory_base const* ) - { - factory.apply(boost::addressof(m_storage)) ; - m_initialized = true ; - } - - template - void assign_expr_to_initialized ( Expr const& factory, in_place_factory_base const* tag ) - { - destroy(); - construct(factory,tag); - } - - // Constructs in-place using the given typed factory - template - void assign_expr_to_initialized ( Expr const& factory, typed_in_place_factory_base const* tag ) - { - destroy(); - construct(factory,tag); - } -#endif - #endif -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Constructs using any expression implicitly convertible to the single argument // of a one-argument T constructor. // Converting constructions of optional from optional uses this function with @@ -421,29 +220,6 @@ class tc_optional_base : public optional_tag { assign_value( boost::forward(expr) ); } -#else - // Constructs using any expression implicitly convertible to the single argument - // of a one-argument T constructor. - // Converting constructions of optional from optional uses this function with - // 'Expr' being of type 'U' and relying on a converting constructor of T from U. - template - void construct ( Expr const& expr, void const* ) - { - m_storage = value_type(expr) ; - m_initialized = true ; - } - - // Assigns using a form any expression implicitly convertible to the single argument - // of a T's assignment operator. - // Converting assignments of optional from optional uses this function with - // 'Expr' being of type 'U' and relying on a converting assignment of T from U. - template - void assign_expr_to_initialized ( Expr const& expr, void const* ) - { - assign_value(expr); - } - -#endif #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION // BCB5.64 (and probably lower versions) workaround. @@ -458,7 +234,7 @@ class tc_optional_base : public optional_tag // For VC<=70 compilers this workaround doesn't work because the compiler issues and error // instead of choosing the wrong overload // -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES + // Notice that 'Expr' will be optional or optional (but not tc_optional_base<..>) template void construct ( Expr&& expr, optional_tag const* ) @@ -471,26 +247,10 @@ class tc_optional_base : public optional_tag m_initialized = true ; } } -#else - // Notice that 'Expr' will be optional or optional (but not tc_optional_base<..>) - template - void construct ( Expr const& expr, optional_tag const* ) - { - if ( expr.is_initialized() ) - { - // An exception can be thrown here. - // It it happens, THIS will be left uninitialized. - m_storage = value_type(expr.get()) ; - m_initialized = true ; - } - } -#endif #endif // defined BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION void assign_value ( argument_type val ) { m_storage = val; } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES void assign_value ( rval_reference_type val ) { m_storage = static_cast(val); } -#endif void destroy() { diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index df3c8a38..b755d911 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -145,10 +145,8 @@ class optional_base : public optional_tag protected: typedef T & reference_type ; typedef T const& reference_const_type ; -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES typedef T && rval_reference_type ; typedef T && reference_type_of_temporary_wrapper ; -#endif typedef T * pointer_type ; typedef T const* pointer_const_type ; typedef T const& argument_type ; @@ -174,7 +172,6 @@ class optional_base : public optional_tag construct(val); } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // move-construct an optional initialized from an rvalue-ref to 'val'. // Can throw if T::T(T&&) does optional_base ( init_value_tag, rval_reference_type val ) @@ -183,7 +180,6 @@ class optional_base : public optional_tag { construct( boost::move(val) ); } -#endif // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. // Can throw if T::T(T const&) does @@ -195,7 +191,6 @@ class optional_base : public optional_tag construct(val); } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Creates an optional initialized with 'move(val)' IFF cond is true, otherwise creates an uninitialized optional. // Can throw if T::T(T &&) does optional_base ( bool cond, rval_reference_type val ) @@ -205,7 +200,6 @@ class optional_base : public optional_tag if ( cond ) construct(boost::move(val)); } -#endif // Creates a deep copy of another optional // Can throw if T::T(T const&) does @@ -217,7 +211,6 @@ class optional_base : public optional_tag construct(rhs.get_impl()); } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Creates a deep move of another optional // Can throw if T::T(T&&) does optional_base ( optional_base&& rhs ) @@ -228,9 +221,7 @@ class optional_base : public optional_tag if ( rhs.is_initialized() ) construct( boost::move(rhs.get_impl()) ); } -#endif -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES template explicit optional_base ( Expr&& expr, PtrExpr const* tag ) @@ -240,34 +231,18 @@ class optional_base : public optional_tag construct(boost::forward(expr),tag); } -#else - // This is used for both converting and in-place constructions. - // Derived classes use the 'tag' to select the appropriate - // implementation (the correct 'construct()' overload) - template - explicit optional_base ( Expr const& expr, Expr const* tag ) - : - m_initialized(false) - { - construct(expr,tag); - } - -#endif - optional_base& operator= ( optional_base const& rhs ) { this->assign(rhs); return *this; } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES optional_base& operator= ( optional_base && rhs ) BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value) { this->assign(static_cast(rhs)); return *this; } -#endif // No-throw (assuming T::~T() doesn't) ~optional_base() { destroy() ; } @@ -288,7 +263,6 @@ class optional_base : public optional_tag } } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Assigns from another optional (deep-moves the rhs value) void assign ( optional_base&& rhs ) { @@ -304,7 +278,6 @@ class optional_base : public optional_tag construct(boost::move(rhs.get_impl())); } } -#endif // Assigns from another _convertible_ optional (deep-copies the rhs value) template @@ -332,7 +305,6 @@ class optional_base : public optional_tag } } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // move-assigns from another _convertible_ optional (deep-moves from the rhs value) template void assign ( optional&& rhs ) @@ -350,7 +322,6 @@ class optional_base : public optional_tag construct(static_cast(rhs.get())); } } -#endif // Assigns from a T (deep-copies the rhs value) void assign ( argument_type val ) @@ -360,7 +331,6 @@ class optional_base : public optional_tag else construct(val); } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Assigns from a T (deep-moves the rhs value) void assign ( rval_reference_type val ) { @@ -368,7 +338,6 @@ class optional_base : public optional_tag assign_value( boost::move(val) ); else construct( boost::move(val) ); } -#endif // Assigns from "none", destroying the current value, if any, leaving this UNINITIALIZED // No-throw (assuming T::~T() doesn't) @@ -376,7 +345,6 @@ class optional_base : public optional_tag #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES template void assign_expr ( Expr&& expr, ExprPtr const* tag ) { @@ -384,15 +352,6 @@ class optional_base : public optional_tag assign_expr_to_initialized(boost::forward(expr),tag); else construct(boost::forward(expr),tag); } -#else - template - void assign_expr ( Expr const& expr, Expr const* tag ) - { - if (is_initialized()) - assign_expr_to_initialized(expr,tag); - else construct(expr,tag); - } -#endif #endif @@ -421,16 +380,13 @@ class optional_base : public optional_tag m_initialized = true ; } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES void construct ( rval_reference_type val ) { ::new (m_storage.address()) unqualified_value_type( boost::move(val) ) ; m_initialized = true ; } -#endif -#if (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) && (!defined BOOST_NO_CXX11_VARIADIC_TEMPLATES) // Constructs in-place // upon exception *this is always uninitialized template @@ -463,154 +419,9 @@ class optional_base : public optional_tag if ( cond ) construct(in_place_init, boost::forward(args)...); } -#elif (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) - template - void construct ( in_place_init_t, Arg&& arg ) - { - ::new (m_storage.address()) unqualified_value_type( boost::forward(arg) ); - m_initialized = true ; - } - - void construct ( in_place_init_t ) - { - ::new (m_storage.address()) unqualified_value_type(); - m_initialized = true ; - } - - template - void emplace_assign ( Arg&& arg ) - { - destroy(); - construct(in_place_init, boost::forward(arg)) ; - } - - void emplace_assign () - { - destroy(); - construct(in_place_init) ; - } - - template - explicit optional_base ( in_place_init_t, Arg&& arg ) - : - m_initialized(false) - { - construct(in_place_init, boost::forward(arg)); - } - - explicit optional_base ( in_place_init_t ) - : - m_initialized(false) - { - construct(in_place_init); - } - - template - explicit optional_base ( in_place_init_if_t, bool cond, Arg&& arg ) - : - m_initialized(false) - { - if ( cond ) - construct(in_place_init, boost::forward(arg)); - } - - explicit optional_base ( in_place_init_if_t, bool cond ) - : - m_initialized(false) - { - if ( cond ) - construct(in_place_init); - } - -#else - - template - void construct ( in_place_init_t, const Arg& arg ) - { - ::new (m_storage.address()) unqualified_value_type( arg ); - m_initialized = true ; - } - - template - void construct ( in_place_init_t, Arg& arg ) - { - ::new (m_storage.address()) unqualified_value_type( arg ); - m_initialized = true ; - } - - void construct ( in_place_init_t ) - { - ::new (m_storage.address()) unqualified_value_type(); - m_initialized = true ; - } - - template - void emplace_assign ( const Arg& arg ) - { - destroy(); - construct(in_place_init, arg); - } - - template - void emplace_assign ( Arg& arg ) - { - destroy(); - construct(in_place_init, arg); - } - - void emplace_assign () - { - destroy(); - construct(in_place_init); - } - - template - explicit optional_base ( in_place_init_t, const Arg& arg ) - : m_initialized(false) - { - construct(in_place_init, arg); - } - - template - explicit optional_base ( in_place_init_t, Arg& arg ) - : m_initialized(false) - { - construct(in_place_init, arg); - } - - explicit optional_base ( in_place_init_t ) - : m_initialized(false) - { - construct(in_place_init); - } - - template - explicit optional_base ( in_place_init_if_t, bool cond, const Arg& arg ) - : m_initialized(false) - { - if ( cond ) - construct(in_place_init, arg); - } - - template - explicit optional_base ( in_place_init_if_t, bool cond, Arg& arg ) - : m_initialized(false) - { - if ( cond ) - construct(in_place_init, arg); - } - - explicit optional_base ( in_place_init_if_t, bool cond ) - : m_initialized(false) - { - if ( cond ) - construct(in_place_init); - } -#endif #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Constructs in-place using the given factory template void construct ( Expr&& factory, in_place_factory_base const* ) @@ -642,42 +453,8 @@ class optional_base : public optional_tag construct(factory,tag); } -#else - // Constructs in-place using the given factory - template - void construct ( Expr const& factory, in_place_factory_base const* ) - { - boost_optional_detail::construct(factory, m_storage.address()); - m_initialized = true ; - } - - // Constructs in-place using the given typed factory - template - void construct ( Expr const& factory, typed_in_place_factory_base const* ) - { - factory.apply(m_storage.address()) ; - m_initialized = true ; - } - - template - void assign_expr_to_initialized ( Expr const& factory, in_place_factory_base const* tag ) - { - destroy(); - construct(factory,tag); - } - - // Constructs in-place using the given typed factory - template - void assign_expr_to_initialized ( Expr const& factory, typed_in_place_factory_base const* tag ) - { - destroy(); - construct(factory,tag); - } -#endif - #endif -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Constructs using any expression implicitly convertible to the single argument // of a one-argument T constructor. // Converting constructions of optional from optional uses this function with @@ -698,29 +475,6 @@ class optional_base : public optional_tag { assign_value( boost::forward(expr) ); } -#else - // Constructs using any expression implicitly convertible to the single argument - // of a one-argument T constructor. - // Converting constructions of optional from optional uses this function with - // 'Expr' being of type 'U' and relying on a converting constructor of T from U. - template - void construct ( Expr const& expr, void const* ) - { - new (m_storage.address()) unqualified_value_type(expr) ; - m_initialized = true ; - } - - // Assigns using a form any expression implicitly convertible to the single argument - // of a T's assignment operator. - // Converting assignments of optional from optional uses this function with - // 'Expr' being of type 'U' and relying on a converting assignment of T from U. - template - void assign_expr_to_initialized ( Expr const& expr, void const* ) - { - assign_value(expr); - } - -#endif #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION // BCB5.64 (and probably lower versions) workaround. @@ -735,7 +489,7 @@ class optional_base : public optional_tag // For VC<=70 compilers this workaround doesn't work because the compiler issues and error // instead of choosing the wrong overload // -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES + // Notice that 'Expr' will be optional or optional (but not optional_base<..>) template void construct ( Expr&& expr, optional_tag const* ) @@ -748,26 +502,10 @@ class optional_base : public optional_tag m_initialized = true ; } } -#else - // Notice that 'Expr' will be optional or optional (but not optional_base<..>) - template - void construct ( Expr const& expr, optional_tag const* ) - { - if ( expr.is_initialized() ) - { - // An exception can be thrown here. - // It it happens, THIS will be left uninitialized. - new (m_storage.address()) unqualified_value_type(expr.get()) ; - m_initialized = true ; - } - } -#endif #endif // defined BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION void assign_value ( argument_type val ) { get_impl() = val; } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES void assign_value ( rval_reference_type val ) { get_impl() = static_cast(val); } -#endif void destroy() { @@ -928,10 +666,8 @@ class optional typedef BOOST_DEDUCED_TYPENAME base::value_type value_type ; typedef BOOST_DEDUCED_TYPENAME base::reference_type reference_type ; typedef BOOST_DEDUCED_TYPENAME base::reference_const_type reference_const_type ; -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES typedef BOOST_DEDUCED_TYPENAME base::rval_reference_type rval_reference_type ; typedef BOOST_DEDUCED_TYPENAME base::reference_type_of_temporary_wrapper reference_type_of_temporary_wrapper ; -#endif typedef BOOST_DEDUCED_TYPENAME base::pointer_type pointer_type ; typedef BOOST_DEDUCED_TYPENAME base::pointer_const_type pointer_const_type ; typedef BOOST_DEDUCED_TYPENAME base::argument_type argument_type ; @@ -948,23 +684,19 @@ class optional // Can throw if T::T(T const&) does optional ( argument_type val ) : base(optional_detail::init_value_tag(), val) {} -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // 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(), boost::forward(val)) {} -#endif // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. // Can throw if T::T(T const&) does optional ( bool cond, argument_type val ) : base(cond,val) {} -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES /// 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, boost::forward(val) ) {} -#endif // NOTE: MSVC needs templated versions first @@ -984,7 +716,6 @@ class optional this->construct(rhs.get()); } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Creates a deep move of another convertible optional // Requires a valid conversion from U to T. // Can throw if T::T(U&&) does @@ -1000,7 +731,6 @@ class optional if ( rhs.is_initialized() ) this->construct( boost::move(rhs.get()) ); } -#endif #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT // Creates an optional with an expression which can be either @@ -1012,7 +742,6 @@ class optional // even though explicit overloads are present for these. // Depending on the above some T ctor is called. // Can throw if the resolved T ctor throws. -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES template @@ -1022,10 +751,6 @@ class optional : base(boost::forward(expr),boost::addressof(expr)) {} -#else - template - explicit optional ( Expr const& expr ) : base(expr,boost::addressof(expr)) {} -#endif // !defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES #endif // !defined BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT // Creates a deep copy of another optional @@ -1036,10 +761,8 @@ class optional optional ( optional const& rhs ) : base( static_cast(rhs) ) {} #endif -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Creates a deep move of another optional // Can throw if T::T(T&&) does - #ifndef BOOST_OPTIONAL_DETAIL_NO_DEFAULTED_MOVE_FUNCTIONS optional ( optional && ) = default; #else @@ -1049,7 +772,6 @@ class optional {} #endif -#endif #if BOOST_WORKAROUND(_MSC_VER, <= 1600) // On old MSVC compilers the implicitly declared dtor is not called @@ -1060,7 +782,6 @@ class optional #if !defined(BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT) && !defined(BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION) // Assigns from an expression. See corresponding constructor. // Basic Guarantee: If the resolved T ctor throws, this is left UNINITIALIZED -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES template BOOST_DEDUCED_TYPENAME boost::enable_if, optional&>::type @@ -1070,14 +791,6 @@ class optional return *this ; } -#else - template - optional& operator= ( Expr const& expr ) - { - this->assign_expr(expr,boost::addressof(expr)); - return *this ; - } -#endif // !defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES #endif // !defined(BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT) && !defined(BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION) // Copy-assigns from another convertible optional (converts && deep-copies the rhs value) @@ -1090,7 +803,6 @@ class optional return *this ; } -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Move-assigns from another convertible optional (converts && deep-moves the rhs value) // Requires a valid conversion from U to T. // Basic Guarantee: If T::T( U && ) throws, this is left UNINITIALIZED @@ -1100,7 +812,6 @@ class optional this->assign(boost::move(rhs)); return *this ; } -#endif // Assigns from another optional (deep-copies the rhs value) // Basic Guarantee: If T::T( T const& ) throws, this is left UNINITIALIZED @@ -1115,7 +826,6 @@ class optional } #endif -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Assigns from another optional (deep-moves the rhs value) #ifndef BOOST_OPTIONAL_DETAIL_NO_DEFAULTED_MOVE_FUNCTIONS optional& operator= ( optional && ) = default; @@ -1128,9 +838,7 @@ class optional } #endif -#endif // BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES -#ifndef BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX // Assigns from a T (deep-moves/copies the rhs value) template @@ -1141,26 +849,6 @@ class optional return *this ; } -#else - - // Assigns from a T (deep-copies the rhs value) - // Basic Guarantee: If T::( T const& ) throws, this is left UNINITIALIZED - optional& operator= ( argument_type val ) - { - this->assign( val ) ; - return *this ; - } - -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES - // Assigns from a T (deep-moves the rhs value) - optional& operator= ( rval_reference_type val ) - { - this->assign( boost::move(val) ) ; - return *this ; - } -#endif - -#endif // BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX // Assigns from a "none" // Which destroys the current value, if any, leaving this UNINITIALIZED @@ -1171,7 +859,6 @@ class optional return *this ; } -#if (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) && (!defined BOOST_NO_CXX11_VARIADIC_TEMPLATES) // Constructs in-place // upon exception *this is always uninitialized template @@ -1190,82 +877,6 @@ class optional : base( in_place_init_if, cond, boost::forward(args)... ) {} -#elif (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) - template - void emplace ( Arg&& arg ) - { - this->emplace_assign( boost::forward(arg) ); - } - - void emplace () - { - this->emplace_assign(); - } - - template - explicit optional ( in_place_init_t, Args&& args ) - : base( in_place_init, boost::forward(args) ) - {} - - explicit optional ( in_place_init_t ) - : base( in_place_init ) - {} - - template - explicit optional ( in_place_init_if_t, bool cond, Args&& args ) - : base( in_place_init_if, cond, boost::forward(args) ) - {} - - explicit optional ( in_place_init_if_t, bool cond ) - : base( in_place_init_if, cond ) - {} -#else - template - void emplace ( const Arg& arg ) - { - this->emplace_assign( arg ); - } - - template - void emplace ( Arg& arg ) - { - this->emplace_assign( arg ); - } - - void emplace () - { - this->emplace_assign(); - } - - template - explicit optional ( in_place_init_t, const Arg& arg ) - : base( in_place_init, arg ) - {} - - template - explicit optional ( in_place_init_t, Arg& arg ) - : base( in_place_init, arg ) - {} - - explicit optional ( in_place_init_t ) - : base( in_place_init ) - {} - - template - explicit optional ( in_place_init_if_t, bool cond, const Arg& arg ) - : base( in_place_init_if, cond, arg ) - {} - - template - explicit optional ( in_place_init_if_t, bool cond, Arg& arg ) - : base( in_place_init_if, cond, arg ) - {} - - explicit optional ( in_place_init_if_t, bool cond ) - : base( in_place_init_if, cond ) - {} -#endif - void swap( optional & arg ) BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value) { @@ -1293,16 +904,10 @@ class optional // Returns a reference to the value if this is initialized, otherwise, // the behaviour is UNDEFINED // No-throw -#if (!defined BOOST_NO_CXX11_REF_QUALIFIERS) && (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) reference_const_type operator *() const& { return this->get() ; } reference_type operator *() & { return this->get() ; } reference_type_of_temporary_wrapper operator *() && { return boost::move(this->get()) ; } -#else - reference_const_type operator *() const { return this->get() ; } - reference_type operator *() { return this->get() ; } -#endif // !defined BOOST_NO_CXX11_REF_QUALIFIERS -#if (!defined BOOST_NO_CXX11_REF_QUALIFIERS) && (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) reference_const_type value() const& { if (this->is_initialized()) @@ -1327,26 +932,8 @@ class optional throw_exception(bad_optional_access()); } -#else - reference_const_type value() const - { - if (this->is_initialized()) - return this->get() ; - else - throw_exception(bad_optional_access()); - } - - reference_type value() - { - if (this->is_initialized()) - return this->get() ; - else - throw_exception(bad_optional_access()); - } -#endif -#ifndef BOOST_NO_CXX11_REF_QUALIFIERS template value_type value_or ( U&& v ) const& { @@ -1364,36 +951,7 @@ class optional else return boost::forward(v); } -#elif !defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES - template - value_type value_or ( U&& v ) const - { - if (this->is_initialized()) - return get(); - else - return boost::forward(v); - } -#else - template - value_type value_or ( U const& v ) const - { - if (this->is_initialized()) - return get(); - else - return v; - } - template - value_type value_or ( U& v ) const - { - if (this->is_initialized()) - return get(); - else - return v; - } -#endif - -#if (!defined BOOST_NO_CXX11_REF_QUALIFIERS) && (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) template value_type value_or_eval ( F f ) const& { @@ -1469,57 +1027,6 @@ class optional return none; } -#else - template - value_type value_or_eval ( F f ) const - { - if (this->is_initialized()) - return get(); - else - return f(); - } - - template - optional::type> - map(F f) - { - if (this->has_value()) - return f(get()); - else - return none; - } - - template - optional::type> - map(F f) const - { - if (this->has_value()) - return f(get()); - else - return none; - } - - template - optional::type> - flat_map(F f) - { - if (this->has_value()) - return f(get()); - else - return none; - } - - template - optional::type> - flat_map(F f) const - { - if (this->has_value()) - return f(get()); - else - return none; - } - -#endif bool has_value() const BOOST_NOEXCEPT { return this->is_initialized() ; } @@ -1527,13 +1034,11 @@ class optional } ; -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES template class optional { BOOST_STATIC_ASSERT_MSG(sizeof(T) == 0, "Optional rvalue references are illegal."); } ; -#endif } // namespace boost @@ -1543,7 +1048,6 @@ class optional namespace boost { -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES template inline @@ -1560,25 +1064,6 @@ optional::type> make_optional ( bool cond return optional::type>(cond,boost::forward(v)); } -#else - -// Returns optional(v) -template -inline -optional make_optional ( T const& v ) -{ - return optional(v); -} - -// Returns optional(cond,v) -template -inline -optional make_optional ( bool cond, T const& v ) -{ - return optional(cond,v); -} - -#endif // BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES // Returns a reference to the value if this is initialized, otherwise, the behaviour is UNDEFINED. // No-throw From ae15fc54b2d9ff2c4845d6ee688e2100038cfc24 Mon Sep 17 00:00:00 2001 From: typenameTea Date: Thu, 19 Sep 2024 22:14:24 +0100 Subject: [PATCH 07/23] refactor: add back unified init syntax conditional --- include/boost/optional/optional.hpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index b755d911..37b5c359 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -838,7 +838,7 @@ class optional } #endif - +#ifndef BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX // Assigns from a T (deep-moves/copies the rhs value) template @@ -849,6 +849,24 @@ class optional return *this ; } +#else + + // Assigns from a T (deep-copies the rhs value) + // Basic Guarantee: If T::( T const& ) throws, this is left UNINITIALIZED + optional& operator= ( argument_type val ) + { + this->assign( val ) ; + return *this ; + } + + // Assigns from a T (deep-moves the rhs value) + optional& operator= ( rval_reference_type val ) + { + this->assign( boost::move(val) ) ; + return *this ; + } + +#endif // BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX // Assigns from a "none" // Which destroys the current value, if any, leaving this UNINITIALIZED From 1820ae55ef915ba82bff9103c67646ba1168b3e6 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Fri, 27 Sep 2024 16:10:56 +0200 Subject: [PATCH 08/23] docs no longer mention perfect forwarding workarounds --- doc/13_convenience.qbk | 2 +- doc/28_ref_optional_semantics.qbk | 6 +++--- doc/90_dependencies.qbk | 35 ------------------------------- 3 files changed, 4 insertions(+), 39 deletions(-) diff --git a/doc/13_convenience.qbk b/doc/13_convenience.qbk index 9a95de3a..0989b5a7 100644 --- a/doc/13_convenience.qbk +++ b/doc/13_convenience.qbk @@ -15,7 +15,7 @@ deductions for class template parameters. std::optional os2 = osv; // OK os2 == osv; // OK -They are practical, ans sometimes stem from the argument for consistency: +They are practical, and sometimes stem from the argument for consistency: if `(optT && *optT == u)` works then `(optT == u)` should also work. However, these intelligent convenience functions sometimes produce results diff --git a/doc/28_ref_optional_semantics.qbk b/doc/28_ref_optional_semantics.qbk index 0fc90a73..9a5986f9 100644 --- a/doc/28_ref_optional_semantics.qbk +++ b/doc/28_ref_optional_semantics.qbk @@ -233,7 +233,7 @@ __SPACE__ arguments `std::forward(args)...`. * [*Postconditions:] `*this` is initialized. * [*Throws:] Any exception thrown by the selected constructor of `T`. -* [*Notes: ] `T` need not be __MOVE_CONSTRUCTIBLE__. On compilers that do not support variadic templates or rvalue references, this constuctor is available in limited functionality. For details [link optional_emplace_workaround see here]. +* [*Notes: ] `T` need not be __MOVE_CONSTRUCTIBLE__. * [*Example:] `` @@ -257,7 +257,7 @@ __SPACE__ * [*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)...`. * [*Postconditions:] `bool(*this) == condition`. * [*Throws:] Any exception thrown by the selected constructor of `T`. -* [*Notes: ] `T` need not be __MOVE_CONSTRUCTIBLE__. On compilers that do not support variadic templates or rvalue references, this constuctor is available in limited functionality. For details [link optional_emplace_workaround see here]. +* [*Notes: ] `T` need not be __MOVE_CONSTRUCTIBLE__. * [*Example:] `` @@ -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`. On compilers that do not support variadic templates or rvalue references, this function is available in limited functionality. For details [link optional_emplace_workaround see here]. +* [*Notes:] `T` need not be __MOVE_CONSTRUCTIBLE__ or `MoveAssignable`. * [*Example:] `` T v; diff --git a/doc/90_dependencies.qbk b/doc/90_dependencies.qbk index acc23c10..365f2be3 100644 --- a/doc/90_dependencies.qbk +++ b/doc/90_dependencies.qbk @@ -24,41 +24,6 @@ The implementation uses the following other Boost modules: [endsect] -[section Emplace operations in older compilers][#optional_emplace_workaround] - -Certain constructors and functions in the interface of `optional` perform a 'perfect forwarding' of arguments: - - template optional(in_place_init_t, Args&&... args); - template optional(in_place_init_if_t, bool condition, Args&&... args); - template void emplace(Args&&... args); - -On compilers that do not support variadic templates, each of these functions is substituted with two overloads, one forwarding a single argument, the other forwarding zero arguments. This forms the following set: - - template optional(in_place_init_t, Arg&& arg); - optional(in_place_init_t); - - template optional(in_place_init_if_t, bool condition, Arg&& arg); - optional(in_place_init_if_t, bool condition); - - template void emplace(Arg&& arg); - void emplace(); - -On compilers that do not support rvalue references, each of these functions is substituted with three overloads: taking `const` and non-`const` lvalue reference, and third forwarding zero arguments. This forms the following set: - - template optional(in_place_init_t, const Arg& arg); - template optional(in_place_init_t, Arg& arg); - optional(in_place_init_t); - - template optional(in_place_init_if_t, bool condition, const Arg& arg); - template optional(in_place_init_if_t, bool condition, Arg& arg); - optional(in_place_init_if_t, bool condition); - - template void emplace(const Arg& arg); - template void emplace(Arg& arg); - void emplace(); - -This workaround addresses about 40% of all use cases. If this is insufficient, you need to resort to using [link boost_optional.design.in_place_factories In-Place Factories]. -[endsect] [section Optional Reference Binding][#optional_reference_binding] From cacde054df5e5547c5c38d6f62299dd2ae50e25f Mon Sep 17 00:00:00 2001 From: typenameTea Date: Sat, 28 Sep 2024 17:18:00 +0100 Subject: [PATCH 09/23] refactor: drop Boost.Move dependency Implement constexpr definitions of forward and move to replace usage of boost::move and boost::forward from the Boost.Move library. Alter tests to use std::move instead of boost::move. Remove the dependency on Boost.Move from build.jam --- CMakeLists.txt | 1 - build.jam | 1 - doc/90_dependencies.qbk | 1 - .../detail/optional_reference_spec.hpp | 2 +- .../boost/optional/detail/optional_swap.hpp | 2 +- .../optional_trivially_copyable_base.hpp | 18 ++-- .../optional/detail/optional_utility.hpp | 38 +++++++++ include/boost/optional/optional.hpp | 82 +++++++++---------- include/boost/optional/optional_io.hpp | 2 +- test/optional_test_flat_map.cpp | 4 +- test/optional_test_map.cpp | 6 +- test/optional_test_move.cpp | 26 +++--- test/optional_test_noexcept_move.cpp | 8 +- 13 files changed, 113 insertions(+), 78 deletions(-) create mode 100644 include/boost/optional/detail/optional_utility.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 976738b5..75ea3364 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,6 @@ target_link_libraries(boost_optional Boost::assert Boost::config Boost::core - Boost::move Boost::static_assert Boost::throw_exception Boost::type_traits diff --git a/build.jam b/build.jam index 5c8089b5..6f1fcf6d 100644 --- a/build.jam +++ b/build.jam @@ -9,7 +9,6 @@ constant boost_dependencies : /boost/assert//boost_assert /boost/config//boost_config /boost/core//boost_core - /boost/move//boost_move /boost/static_assert//boost_static_assert /boost/throw_exception//boost_throw_exception /boost/type_traits//boost_type_traits ; diff --git a/doc/90_dependencies.qbk b/doc/90_dependencies.qbk index 365f2be3..8f849a19 100644 --- a/doc/90_dependencies.qbk +++ b/doc/90_dependencies.qbk @@ -17,7 +17,6 @@ The implementation uses the following other Boost modules: # assert # config # core -# move # static_assert # throw_exception # type_traits diff --git a/include/boost/optional/detail/optional_reference_spec.hpp b/include/boost/optional/detail/optional_reference_spec.hpp index 9cb2b5c1..30824c26 100644 --- a/include/boost/optional/detail/optional_reference_spec.hpp +++ b/include/boost/optional/detail/optional_reference_spec.hpp @@ -39,7 +39,7 @@ BOOST_DEDUCED_TYPENAME boost::remove_reference::type& forward_reference(T&& r { BOOST_STATIC_ASSERT_MSG(boost::is_lvalue_reference::value, "binding rvalue references to optional lvalue references is disallowed"); - return boost::forward(r); + return optional_detail::forward(r); } #endif // BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES diff --git a/include/boost/optional/detail/optional_swap.hpp b/include/boost/optional/detail/optional_swap.hpp index f4eceabb..0c708c6c 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_) boost::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 a8e0371c..3a08f7d8 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(boost::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(boost::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( boost::forward(args)... ) ; + m_storage = value_type( optional_detail::forward(args)... ) ; m_initialized = true ; } template void emplace_assign ( Args&&... args ) { - construct(in_place_init, boost::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, boost::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, boost::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(boost::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( boost::forward(expr) ); + assign_value( optional_detail::forward(expr) ); } #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION @@ -243,7 +243,7 @@ class tc_optional_base : public optional_tag { // An exception can be thrown here. // It it happens, THIS will be left uninitialized. - m_storage = value_type(boost::move(expr.get())) ; + m_storage = value_type(optional_detail::move(expr.get())) ; m_initialized = true ; } } diff --git a/include/boost/optional/detail/optional_utility.hpp b/include/boost/optional/detail/optional_utility.hpp new file mode 100644 index 00000000..6fd2fd2a --- /dev/null +++ b/include/boost/optional/detail/optional_utility.hpp @@ -0,0 +1,38 @@ +// Copyright (C) 2024 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/libs/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com + +#ifndef BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_HPP +#define BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_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 +{ + BOOST_STATIC_ASSERT_MSG(!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 37b5c359..6aec3cec 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -51,7 +51,6 @@ #include #include #include -#include #include #include @@ -59,6 +58,7 @@ #include #include #include +#include namespace boost { namespace optional_detail { @@ -178,7 +178,7 @@ class optional_base : public optional_tag : m_initialized(false) { - construct( boost::move(val) ); + construct( optional_detail::move(val) ); } // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. @@ -198,7 +198,7 @@ class optional_base : public optional_tag m_initialized(false) { if ( cond ) - construct(boost::move(val)); + construct(optional_detail::move(val)); } // Creates a deep copy of another optional @@ -219,7 +219,7 @@ class optional_base : public optional_tag m_initialized(false) { if ( rhs.is_initialized() ) - construct( boost::move(rhs.get_impl()) ); + construct( optional_detail::move(rhs.get_impl()) ); } @@ -228,7 +228,7 @@ class optional_base : public optional_tag : m_initialized(false) { - construct(boost::forward(expr),tag); + construct(optional_detail::forward(expr),tag); } optional_base& operator= ( optional_base const& rhs ) @@ -269,13 +269,13 @@ class optional_base : public optional_tag if (is_initialized()) { if ( rhs.is_initialized() ) - assign_value( boost::move(rhs.get_impl()) ); + assign_value( optional_detail::move(rhs.get_impl()) ); else destroy(); } else { if ( rhs.is_initialized() ) - construct(boost::move(rhs.get_impl())); + construct(optional_detail::move(rhs.get_impl())); } } @@ -335,8 +335,8 @@ class optional_base : public optional_tag void assign ( rval_reference_type val ) { if (is_initialized()) - assign_value( boost::move(val) ); - else construct( boost::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 @@ -349,8 +349,8 @@ class optional_base : public optional_tag void assign_expr ( Expr&& expr, ExprPtr const* tag ) { if (is_initialized()) - assign_expr_to_initialized(boost::forward(expr),tag); - else construct(boost::forward(expr),tag); + assign_expr_to_initialized(optional_detail::forward(expr),tag); + else construct(optional_detail::forward(expr),tag); } #endif @@ -382,7 +382,7 @@ class optional_base : public optional_tag void construct ( rval_reference_type val ) { - ::new (m_storage.address()) unqualified_value_type( boost::move(val) ) ; + ::new (m_storage.address()) unqualified_value_type( optional_detail::move(val) ) ; m_initialized = true ; } @@ -392,7 +392,7 @@ class optional_base : public optional_tag template void construct ( in_place_init_t, Args&&... args ) { - ::new (m_storage.address()) unqualified_value_type( boost::forward(args)... ) ; + ::new (m_storage.address()) unqualified_value_type( optional_detail::forward(args)... ) ; m_initialized = true ; } @@ -400,7 +400,7 @@ class optional_base : public optional_tag void emplace_assign ( Args&&... args ) { destroy(); - construct(in_place_init, boost::forward(args)...); + construct(in_place_init, optional_detail::forward(args)...); } template @@ -408,7 +408,7 @@ class optional_base : public optional_tag : m_initialized(false) { - construct(in_place_init, boost::forward(args)...); + construct(in_place_init, optional_detail::forward(args)...); } template @@ -417,7 +417,7 @@ class optional_base : public optional_tag m_initialized(false) { if ( cond ) - construct(in_place_init, boost::forward(args)...); + construct(in_place_init, optional_detail::forward(args)...); } #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT @@ -462,7 +462,7 @@ class optional_base : public optional_tag template void construct ( Expr&& expr, void const* ) { - new (m_storage.address()) unqualified_value_type(boost::forward(expr)) ; + new (m_storage.address()) unqualified_value_type(optional_detail::forward(expr)) ; m_initialized = true ; } @@ -473,7 +473,7 @@ class optional_base : public optional_tag template void assign_expr_to_initialized ( Expr&& expr, void const* ) { - assign_value( boost::forward(expr) ); + assign_value( optional_detail::forward(expr) ); } #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION @@ -498,7 +498,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(boost::move(expr.get())) ; + new (m_storage.address()) unqualified_value_type(optional_detail::move(expr.get())) ; m_initialized = true ; } } @@ -686,7 +686,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(), boost::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. @@ -695,7 +695,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, boost::forward(val) ) + optional ( bool cond, rval_reference_type val ) : base( cond, optional_detail::forward(val) ) {} // NOTE: MSVC needs templated versions first @@ -729,7 +729,7 @@ class optional base() { if ( rhs.is_initialized() ) - this->construct( boost::move(rhs.get()) ); + this->construct( optional_detail::move(rhs.get()) ); } #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT @@ -748,7 +748,7 @@ class optional explicit optional ( Expr&& expr, BOOST_DEDUCED_TYPENAME boost::enable_if< optional_detail::is_optional_val_init_candidate, bool>::type = true ) - : base(boost::forward(expr),boost::addressof(expr)) + : base(optional_detail::forward(expr),boost::addressof(expr)) {} #endif // !defined BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT @@ -768,7 +768,7 @@ class optional #else optional ( optional && rhs ) BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value) - : base( boost::move(rhs) ) + : base( optional_detail::move(rhs) ) {} #endif @@ -787,7 +787,7 @@ class optional BOOST_DEDUCED_TYPENAME boost::enable_if, optional&>::type operator= ( Expr&& expr ) { - this->assign_expr(boost::forward(expr),boost::addressof(expr)); + this->assign_expr(optional_detail::forward(expr),boost::addressof(expr)); return *this ; } @@ -809,7 +809,7 @@ class optional template optional& operator= ( optional && rhs ) { - this->assign(boost::move(rhs)); + this->assign(optional_detail::move(rhs)); return *this ; } @@ -845,7 +845,7 @@ class optional BOOST_DEDUCED_TYPENAME boost::enable_if::type>, optional&>::type operator= ( T_&& val ) { - this->assign( boost::forward(val) ) ; + this->assign( optional_detail::forward(val) ) ; return *this ; } @@ -862,7 +862,7 @@ class optional // Assigns from a T (deep-moves the rhs value) optional& operator= ( rval_reference_type val ) { - this->assign( boost::move(val) ) ; + this->assign( optional_detail::move(val) ) ; return *this ; } @@ -882,17 +882,17 @@ class optional template void emplace ( Args&&... args ) { - this->emplace_assign( boost::forward(args)... ); + this->emplace_assign( optional_detail::forward(args)... ); } template explicit optional ( in_place_init_t, Args&&... args ) - : base( in_place_init, boost::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, boost::forward(args)... ) + : base( in_place_init_if, cond, optional_detail::forward(args)... ) {} void swap( optional & arg ) @@ -924,7 +924,7 @@ class optional // No-throw reference_const_type operator *() const& { return this->get() ; } reference_type operator *() & { return this->get() ; } - reference_type_of_temporary_wrapper operator *() && { return boost::move(this->get()) ; } + reference_type_of_temporary_wrapper operator *() && { return optional_detail::move(this->get()) ; } reference_const_type value() const& { @@ -945,7 +945,7 @@ class optional reference_type_of_temporary_wrapper value() && { if (this->is_initialized()) - return boost::move(this->get()) ; + return optional_detail::move(this->get()) ; else throw_exception(bad_optional_access()); } @@ -958,16 +958,16 @@ class optional if (this->is_initialized()) return get(); else - return boost::forward(v); + return optional_detail::forward(v); } template value_type value_or ( U&& v ) && { if (this->is_initialized()) - return boost::move(get()); + return optional_detail::move(get()); else - return boost::forward(v); + return optional_detail::forward(v); } template @@ -983,7 +983,7 @@ class optional value_type value_or_eval ( F f ) && { if (this->is_initialized()) - return boost::move(get()); + return optional_detail::move(get()); else return f(); } @@ -1010,7 +1010,7 @@ class optional optional::type> map(F f) && { if (this->has_value()) - return f(boost::move(this->get())); + return f(optional_detail::move(this->get())); else return none; } @@ -1040,7 +1040,7 @@ class optional flat_map(F f) && { if (this->has_value()) - return f(boost::move(get())); + return f(optional_detail::move(get())); else return none; } @@ -1071,7 +1071,7 @@ template inline optional::type> make_optional ( T && v ) { - return optional::type>(boost::forward(v)); + return optional::type>(optional_detail::forward(v)); } // Returns optional(cond,v) @@ -1079,7 +1079,7 @@ template inline optional::type> make_optional ( bool cond, T && v ) { - return optional::type>(cond,boost::forward(v)); + return optional::type>(cond,optional_detail::forward(v)); } diff --git a/include/boost/optional/optional_io.hpp b/include/boost/optional/optional_io.hpp index 3db6ddae..b9ffc254 100644 --- a/include/boost/optional/optional_io.hpp +++ b/include/boost/optional/optional_io.hpp @@ -64,7 +64,7 @@ operator>>(std::basic_istream& in, optional& v) T x; in >> x; #ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES - v = boost::move(x); + v = std::move(x); #else v = x; #endif diff --git a/test/optional_test_flat_map.cpp b/test/optional_test_flat_map.cpp index 05ed63aa..14311b5b 100644 --- a/test/optional_test_flat_map.cpp +++ b/test/optional_test_flat_map.cpp @@ -240,8 +240,8 @@ void test_flat_map_move_only() { { optional om (makeMoveOnly(1)), om2 (makeMoveOnly(2)); - verify_type >(boost::move(om).flat_map(get_val)); - optional oi = boost::move(om2).flat_map(get_val); + verify_type >(std::move(om).flat_map(get_val)); + optional oi = std::move(om2).flat_map(get_val); BOOST_TEST(bool(oi)); BOOST_TEST_EQ(2, *oi); } diff --git a/test/optional_test_map.cpp b/test/optional_test_map.cpp index f0b7f0e2..61a1824d 100644 --- a/test/optional_test_map.cpp +++ b/test/optional_test_map.cpp @@ -62,8 +62,8 @@ int get_val(MoveOnly m) void test_map_move_only() { optional om (makeMoveOnly(7)), om2 (makeMoveOnly(8)); - verify_type >(boost::move(om).map(get_val)); - optional oi = boost::move(om2).map(get_val); + verify_type >(std::move(om).map(get_val)); + optional oi = std::move(om2).map(get_val); BOOST_TEST(bool(oi)); BOOST_TEST_EQ(8, *oi); @@ -72,7 +72,7 @@ void test_map_move_only() BOOST_TEST_EQ(4, *oj); optional o_; - optional oi_ = boost::move(o_).map(get_val); + optional oi_ = std::move(o_).map(get_val); BOOST_TEST(!oi_); } diff --git a/test/optional_test_move.cpp b/test/optional_test_move.cpp index f27cd998..5561d92e 100644 --- a/test/optional_test_move.cpp +++ b/test/optional_test_move.cpp @@ -82,7 +82,7 @@ void test_move_ctor_from_U() BOOST_TEST(o2->s == sValueCopyConstructed || o2->s == sCopyConstructed || o2->s == sMoveConstructed ); BOOST_TEST(v1.s == sIntConstructed); - optional o3 (boost::move(v1)); + optional o3 (std::move(v1)); BOOST_TEST(o3); BOOST_TEST(o3->s == sValueMoveConstructed || o3->s == sMoveConstructed); BOOST_TEST(v1.s == sMovedFrom); @@ -100,7 +100,7 @@ void test_move_ctor_form_T() BOOST_TEST(o2->s == sCopyConstructed); BOOST_TEST(v1.s == sDefaultConstructed); - optional o3 (boost::move(v1)); + optional o3 (std::move(v1)); BOOST_TEST(o3); BOOST_TEST(o3->s == sMoveConstructed); BOOST_TEST(v1.s == sMovedFrom); @@ -109,13 +109,13 @@ void test_move_ctor_form_T() void test_move_ctor_from_optional_T() { optional o1; - optional o2(boost::move(o1)); + optional o2(std::move(o1)); BOOST_TEST(!o1); BOOST_TEST(!o2); optional o3((Oracle())); - optional o4(boost::move(o3)); + optional o4(std::move(o3)); BOOST_TEST(o3); BOOST_TEST(o4); BOOST_TEST(o3->s == sMovedFrom); @@ -156,7 +156,7 @@ void test_move_assign_from_U() BOOST_TEST(v1.s == sIntConstructed); optional o3; - o3 = boost::move(v1); + o3 = std::move(v1); BOOST_TEST(o3); BOOST_TEST(o3->s == sValueMoveConstructed); BOOST_TEST(v1.s == sMovedFrom); @@ -185,7 +185,7 @@ void test_move_assign_from_T() BOOST_TEST(v1.s == sDefaultConstructed); optional o3; - o3 = boost::move(v1); + o3 = std::move(v1); BOOST_TEST(o3); BOOST_TEST(o3->s == sMoveConstructed); BOOST_TEST(v1.s == sMovedFrom); @@ -204,7 +204,7 @@ void test_move_assign_from_optional_T() BOOST_TEST(o1); BOOST_TEST(o1->s == sCopyConstructed); - o2 = boost::move(o3); + o2 = std::move(o3); BOOST_TEST(o3); BOOST_TEST(o3->s == sMovedFrom); BOOST_TEST(o2); @@ -236,15 +236,15 @@ void test_with_move_only() optional o2((MoveOnly(1))); BOOST_TEST(o2); BOOST_TEST(o2->val == 1); - optional o3 (boost::move(o1)); + optional o3 (std::move(o1)); BOOST_TEST(!o3); - optional o4 (boost::move(o2)); + optional o4 (std::move(o2)); BOOST_TEST(o4); BOOST_TEST(o4->val == 1); BOOST_TEST(o2); BOOST_TEST(o2->val == 0); - o3 = boost::move(o4); + o3 = std::move(o4); BOOST_TEST(o3); BOOST_TEST(o3->val == 1); BOOST_TEST(o4); @@ -272,7 +272,7 @@ void test_move_assign_from_optional_U() { optional a((MoveOnly(2))); optional b1; - b1 = boost::move(a); + b1 = std::move(a); BOOST_TEST(b1); BOOST_TEST(b1->val == 2); @@ -288,7 +288,7 @@ void test_move_assign_from_optional_U() void test_move_ctor_from_optional_U() { optional a((MoveOnly(2))); - optional b1(boost::move(a)); + optional b1(std::move(a)); BOOST_TEST(b1); BOOST_TEST(b1->val == 2); @@ -323,7 +323,7 @@ void test_optional_ref_to_movables() BOOST_TEST(m.val == 1); BOOST_TEST(orm->val == 1); - optional orm3 = boost::move(orm); + optional orm3 = std::move(orm); orm3->val = 4; BOOST_TEST(m.val == 4); BOOST_TEST(orm->val == 4); diff --git a/test/optional_test_noexcept_move.cpp b/test/optional_test_noexcept_move.cpp index 258bacd2..bc4fa97a 100644 --- a/test/optional_test_noexcept_move.cpp +++ b/test/optional_test_noexcept_move.cpp @@ -89,19 +89,19 @@ void test_noexcept_optional_with_operator() // compile-time test ONx0 onx0; BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx2() )); - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx2(boost::move(onx2)) )); + BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx2(std::move(onx2)) )); BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( onx2 = ONx2() )); BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxC() )); - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxC(boost::move(onxC)) )); + BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxC(std::move(onxC)) )); BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onxC = ONxC() )); BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxA() )); - BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( ONxA(boost::move(onxA)) )); + BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( ONxA(std::move(onxA)) )); BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onxA = ONxA() )); BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx0() )); - BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( ONx0(boost::move(onx0)) )); + BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( ONx0(std::move(onx0)) )); BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onx0 = ONx0() )); } From 825f38f5b5a7ec83e96906f12bc31f8b9b14ea47 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Fri, 4 Oct 2024 20:07:11 +0200 Subject: [PATCH 10/23] CML: Create header-only target in IDEs Since CMake 3.19 it is possible to specify private sources for `INTERFACE` targets. This makes the library turn up like compiled libraries in IDEs such as Visual Studio. It can also be specified in the `add_library` call but using `target_sources` is more isolated to the CMake version check --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 976738b5..79540d43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,10 @@ add_library(boost_optional INTERFACE) add_library(Boost::optional ALIAS boost_optional) target_include_directories(boost_optional INTERFACE include) +if(NOT CMAKE_VERSION VERSION_LESS "3.19") + file(GLOB_RECURSE headers include/*.hpp) + target_sources(boost_optional PRIVATE ${headers}) +endif() target_link_libraries(boost_optional INTERFACE From cfcd23d55b0dc4e9e3a88c0a3db9fdd4224b2ccc Mon Sep 17 00:00:00 2001 From: typenametea Date: Fri, 4 Oct 2024 21:02:26 +0100 Subject: [PATCH 11/23] docs: Update release notes --- doc/92_relnotes.qbk | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/92_relnotes.qbk b/doc/92_relnotes.qbk index ed16e736..649cad78 100644 --- a/doc/92_relnotes.qbk +++ b/doc/92_relnotes.qbk @@ -16,6 +16,7 @@ * *Breaking change.* Dropped support for C++03. C++11 is now the required minimum. * Dropped dependency on Boost.Utility. * Dropped dependency on Boost.Predef. +* Dropped dependency on Boost.Move. * A bit faster implementation of some relational operations. * 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]. From c33708c1f99bfda566c00ed4d0894a8535701b5c Mon Sep 17 00:00:00 2001 From: typenameTea Date: Sat, 5 Oct 2024 12:47:10 +0100 Subject: [PATCH 12/23] refactor: address code review comments --- include/boost/optional/detail/optional_utility.hpp | 13 ++++++++----- include/boost/optional/optional_io.hpp | 6 +----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/boost/optional/detail/optional_utility.hpp b/include/boost/optional/detail/optional_utility.hpp index 6fd2fd2a..a5ef508b 100644 --- a/include/boost/optional/detail/optional_utility.hpp +++ b/include/boost/optional/detail/optional_utility.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Andrzej Krzemienski. +// Copyright (C) 2024 typenameTea. // // 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,7 +7,7 @@ // See http://www.boost.org/libs/optional for documentation. // // You are welcome to contact the author at: -// akrzemi1@gmail.com +// typenametea@gmail.com #ifndef BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_HPP #define BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_HPP @@ -16,18 +16,21 @@ 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 +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 +template +inline constexpr T&& forward(typename boost::remove_reference::type&& t) noexcept { BOOST_STATIC_ASSERT_MSG(!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 +template +inline constexpr typename boost::remove_reference::type&& move(T&& t) noexcept { return static_cast::type&&>(t); } diff --git a/include/boost/optional/optional_io.hpp b/include/boost/optional/optional_io.hpp index b9ffc254..85acfa17 100644 --- a/include/boost/optional/optional_io.hpp +++ b/include/boost/optional/optional_io.hpp @@ -63,11 +63,7 @@ operator>>(std::basic_istream& in, optional& v) { T x; in >> x; -#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES - v = std::move(x); -#else - v = x; -#endif + v = optional_detail::move(x); } else { From 7974207c7f148ab2f5489323f0509dd44c2b4a38 Mon Sep 17 00:00:00 2001 From: typenameTea Date: Sat, 5 Oct 2024 19:00:27 +0100 Subject: [PATCH 13/23] refactor: make include guard more unique --- include/boost/optional/detail/optional_utility.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/optional/detail/optional_utility.hpp b/include/boost/optional/detail/optional_utility.hpp index a5ef508b..d17babb4 100644 --- a/include/boost/optional/detail/optional_utility.hpp +++ b/include/boost/optional/detail/optional_utility.hpp @@ -9,8 +9,8 @@ // You are welcome to contact the author at: // typenametea@gmail.com -#ifndef BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_HPP -#define BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_HPP +#ifndef BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_TNT_05OCT2024_HPP +#define BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_TNT_05OCT2024_HPP namespace boost { namespace optional_detail { From c8bd18d6dee9ca3cc41307cc8fdb5575df5ad399 Mon Sep 17 00:00:00 2001 From: Ryan Malcolm Underwood Date: Sun, 6 Oct 2024 20:13:05 +0100 Subject: [PATCH 14/23] refactor: update copyright, modify forward assert --- include/boost/optional/detail/optional_utility.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/boost/optional/detail/optional_utility.hpp b/include/boost/optional/detail/optional_utility.hpp index d17babb4..581a7e7c 100644 --- a/include/boost/optional/detail/optional_utility.hpp +++ b/include/boost/optional/detail/optional_utility.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 typenameTea. +// 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 @@ -9,8 +9,8 @@ // You are welcome to contact the author at: // typenametea@gmail.com -#ifndef BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_TNT_05OCT2024_HPP -#define BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_TNT_05OCT2024_HPP +#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 { @@ -25,7 +25,9 @@ inline constexpr T&& forward(typename boost::remove_reference::type& t) noexc template inline constexpr T&& forward(typename boost::remove_reference::type&& t) noexcept { - BOOST_STATIC_ASSERT_MSG(!boost::is_lvalue_reference::value, "Can not forward an rvalue as an lvalue."); +#ifndef BOOST_NO_CXX11_STATIC_ASSERT + static_assert(!boost::is_lvalue_reference::value, "Can not forward an rvalue as an lvalue."); +#endif return static_cast(t); } From b2c7f93ead745dd1d75d76432c3b2be03fa3e2c9 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Wed, 16 Oct 2024 22:51:06 +0200 Subject: [PATCH 15/23] Restore support for compilers without ref qualifiers --- include/boost/none_t.hpp | 13 +++- .../boost/optional/detail/optional_config.hpp | 13 +++- .../boost/optional/detail/optional_swap.hpp | 4 +- include/boost/optional/optional.hpp | 68 +++++++++---------- 4 files changed, 56 insertions(+), 42 deletions(-) diff --git a/include/boost/none_t.hpp b/include/boost/none_t.hpp index 0f95128f..1c8e0761 100644 --- a/include/boost/none_t.hpp +++ b/include/boost/none_t.hpp @@ -14,11 +14,18 @@ #define BOOST_NONE_T_17SEP2003_HPP #include +#include -#if defined (BOOST_NO_CXX11_RVALUE_REFERENCES) || defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_NO_CXX11_REF_QUALIFIERS) \ -|| defined(BOOST_NO_CXX11_LAMBDAS) || defined(BOOST_NO_CXX11_DECLTYPE_N3276) || defined(BOOST_NO_CXX11_NOEXCEPT) || defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) || defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || defined(BOOST_NO_CXX11_DEFAULTED_MOVES) || defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) +#if defined (BOOST_NO_CXX11_RVALUE_REFERENCES) || defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) \ +|| defined(BOOST_NO_CXX11_LAMBDAS) || defined(BOOST_NO_CXX11_DECLTYPE_N3276) \ +|| defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) || defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) \ +|| defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) || defined(BOOST_NO_CXX11_STATIC_ASSERT) -#error "Boost.Optional requires C++11 or later. If you have an older C++ version use Boost.Optional version 1.86 or earlier." +#error "Boost.Optional requires some C++11 features since version 1.87. If you have an older C++ version use Boost.Optional version 1.86 or earlier." + +#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.") #endif diff --git a/include/boost/optional/detail/optional_config.hpp b/include/boost/optional/detail/optional_config.hpp index 3e5e282f..b7ccc3e6 100644 --- a/include/boost/optional/detail/optional_config.hpp +++ b/include/boost/optional/detail/optional_config.hpp @@ -16,7 +16,7 @@ #include #include -#if (defined BOOST_NO_CXX11_RVALUE_REFERENCES) || (defined BOOST_OPTIONAL_CONFIG_NO_RVALUE_REFERENCES) +#if (defined BOOST_OPTIONAL_CONFIG_NO_RVALUE_REFERENCES) # define BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES #endif @@ -82,7 +82,7 @@ #endif // defined(__GNUC__) -#if (defined __GNUC__) && (!defined BOOST_NO_CXX11_RVALUE_REFERENCES) +#if (defined __GNUC__) // On some initial rvalue reference implementations GCC does it in a strange way, // preferring perfect-forwarding constructor to implicit copy constructor. @@ -132,4 +132,13 @@ #endif +#ifdef BOOST_NO_CXX11_REF_QUALIFIERS +# define BOOST_OPTIONAL_CONST_REF_QUAL const +# define BOOST_OPTIONAL_REF_QUAL +#else +# define BOOST_OPTIONAL_CONST_REF_QUAL const& +# define BOOST_OPTIONAL_REF_QUAL & +#endif + + #endif // header guard diff --git a/include/boost/optional/detail/optional_swap.hpp b/include/boost/optional/detail/optional_swap.hpp index 0c708c6c..f1d301b4 100644 --- a/include/boost/optional/detail/optional_swap.hpp +++ b/include/boost/optional/detail/optional_swap.hpp @@ -63,7 +63,7 @@ template <> struct swap_selector { template - static void optional_swap ( optional& x, optional& y ) + static void optional_swap ( optional& x, optional& y ) //BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && BOOST_NOEXCEPT_EXPR(boost::core::invoke_swap(*x, *y))) { if (x) @@ -91,7 +91,7 @@ struct swap_selector } // namespace optional_detail -#if (!defined BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined BOOST_CONFIG_RESTORE_OBSOLETE_SWAP_IMPLEMENTATION) +#if (!defined BOOST_CONFIG_RESTORE_OBSOLETE_SWAP_IMPLEMENTATION) template struct optional_swap_should_use_default_constructor : boost::false_type {} ; diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 6aec3cec..afe42da8 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -580,7 +580,6 @@ struct is_optional_constructible : boost::true_type #if !defined(BOOST_NO_CXX11_DECLTYPE) && !BOOST_WORKAROUND(BOOST_MSVC, < 1800) // for is_assignable -#if (!defined BOOST_NO_CXX11_RVALUE_REFERENCES) // On some initial rvalue reference implementations GCC does it in a strange way, // preferring perfect-forwarding constructor to implicit copy constructor. @@ -591,15 +590,6 @@ struct is_opt_assignable #else -template -struct is_opt_assignable - : boost::conjunction, boost::is_assignable > -{}; - -#endif - -#else - template struct is_opt_assignable : boost::is_convertible {}; @@ -922,11 +912,14 @@ class optional // Returns a reference to the value if this is initialized, otherwise, // the behaviour is UNDEFINED // No-throw - reference_const_type operator *() const& { return this->get() ; } - reference_type operator *() & { return this->get() ; } + reference_const_type operator *() BOOST_OPTIONAL_CONST_REF_QUAL { return this->get() ; } + 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()) ; } +#endif - reference_const_type value() const& + reference_const_type value() BOOST_OPTIONAL_CONST_REF_QUAL { if (this->is_initialized()) return this->get() ; @@ -934,7 +927,7 @@ class optional throw_exception(bad_optional_access()); } - reference_type value() & + reference_type value() BOOST_OPTIONAL_REF_QUAL { if (this->is_initialized()) return this->get() ; @@ -942,41 +935,40 @@ class optional throw_exception(bad_optional_access()); } - reference_type_of_temporary_wrapper value() && + template + value_type value_or ( U&& v ) BOOST_OPTIONAL_CONST_REF_QUAL { if (this->is_initialized()) - return optional_detail::move(this->get()) ; + return get(); else - throw_exception(bad_optional_access()); + return optional_detail::forward(v); } - - - template - value_type value_or ( U&& v ) const& + template + value_type value_or_eval ( F f ) BOOST_OPTIONAL_CONST_REF_QUAL { if (this->is_initialized()) return get(); else - return optional_detail::forward(v); + return f(); } - template - value_type value_or ( U&& v ) && +#ifndef BOOST_NO_CXX11_REF_QUALIFIERS + reference_type_of_temporary_wrapper value() && { if (this->is_initialized()) - return optional_detail::move(get()); + return optional_detail::move(this->get()) ; else - return optional_detail::forward(v); + throw_exception(bad_optional_access()); } - template - value_type value_or_eval ( F f ) const& + template + value_type value_or ( U&& v ) && { if (this->is_initialized()) - return get(); + return optional_detail::move(get()); else - return f(); + return optional_detail::forward(v); } template @@ -987,9 +979,12 @@ class optional else return f(); } +#endif + +// Monadic interface template - optional::type> map(F f) & + optional::type> map(F f) BOOST_OPTIONAL_REF_QUAL { if (this->has_value()) return f(get()); @@ -998,7 +993,7 @@ class optional } template - optional::type> map(F f) const& + optional::type> map(F f) BOOST_OPTIONAL_CONST_REF_QUAL { if (this->has_value()) return f(get()); @@ -1006,6 +1001,7 @@ class optional return none; } +#ifndef BOOST_NO_CXX11_REF_QUALIFIERS template optional::type> map(F f) && { @@ -1014,10 +1010,11 @@ class optional else return none; } +#endif template optional::type> - flat_map(F f) & + flat_map(F f) BOOST_OPTIONAL_REF_QUAL { if (this->has_value()) return f(get()); @@ -1027,7 +1024,7 @@ class optional template optional::type> - flat_map(F f) const& + flat_map(F f) BOOST_OPTIONAL_CONST_REF_QUAL { if (this->has_value()) return f(get()); @@ -1035,6 +1032,7 @@ class optional return none; } +#ifndef BOOST_NO_CXX11_REF_QUALIFIERS template optional::type> flat_map(F f) && @@ -1044,7 +1042,7 @@ class optional else return none; } - +#endif bool has_value() const BOOST_NOEXCEPT { return this->is_initialized() ; } From e601f1ef2d5ca3d5886399ab47a29e6f9df54100 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Wed, 16 Oct 2024 23:26:44 +0200 Subject: [PATCH 16/23] Drop dependency on Boost.StaticAssert --- CMakeLists.txt | 3 +- build.jam | 2 - doc/92_relnotes.qbk | 3 +- .../detail/optional_reference_spec.hpp | 12 +-- include/boost/optional/optional.hpp | 4 +- test/optional_test_convert_assign.cpp | 6 +- test/optional_test_convert_from_T.cpp | 16 ++-- test/optional_test_noexcept_move.cpp | 86 +++++++++---------- test/optional_test_path_assignment.cpp | 2 +- ...ref_convert_assign_const_int_prevented.cpp | 2 +- test/optional_test_sfinae_friendly_ctor.cpp | 24 +++--- 11 files changed, 78 insertions(+), 82 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a91e7a03..80799e10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ add_library(boost_optional INTERFACE) add_library(Boost::optional ALIAS boost_optional) target_include_directories(boost_optional INTERFACE include) -if(NOT CMAKE_VERSION VERSION_LESS "3.19") +if(NOT CMAKE_VERSION VERSION_LESS "3.19") file(GLOB_RECURSE headers include/*.hpp) target_sources(boost_optional PRIVATE ${headers}) endif() @@ -21,7 +21,6 @@ target_link_libraries(boost_optional Boost::assert Boost::config Boost::core - Boost::static_assert Boost::throw_exception Boost::type_traits ) diff --git a/build.jam b/build.jam index 6f1fcf6d..b08ea0b0 100644 --- a/build.jam +++ b/build.jam @@ -9,7 +9,6 @@ constant boost_dependencies : /boost/assert//boost_assert /boost/config//boost_config /boost/core//boost_core - /boost/static_assert//boost_static_assert /boost/throw_exception//boost_throw_exception /boost/type_traits//boost_type_traits ; @@ -22,4 +21,3 @@ explicit call-if : boost-library optional ; - diff --git a/doc/92_relnotes.qbk b/doc/92_relnotes.qbk index 649cad78..e5750753 100644 --- a/doc/92_relnotes.qbk +++ b/doc/92_relnotes.qbk @@ -13,9 +13,10 @@ [heading Boost Release 1.87] -* *Breaking change.* Dropped support for C++03. C++11 is now the required minimum. +* *Breaking change.* Dropped support for C++03. C++11 is now the required minimum; at least some C++11 features. * Dropped dependency on Boost.Utility. * Dropped dependency on Boost.Predef. +* Dropped dependency on Boost.StaticAssert. * Dropped dependency on Boost.Move. * A bit faster implementation of some relational operations. * 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/optional_reference_spec.hpp b/include/boost/optional/detail/optional_reference_spec.hpp index 30824c26..d8405f82 100644 --- a/include/boost/optional/detail/optional_reference_spec.hpp +++ b/include/boost/optional/detail/optional_reference_spec.hpp @@ -29,16 +29,16 @@ template void prevent_binding_rvalue() { #ifndef BOOST_OPTIONAL_CONFIG_ALLOW_BINDING_TO_RVALUES - BOOST_STATIC_ASSERT_MSG(boost::is_lvalue_reference::value, - "binding rvalue references to optional lvalue references is disallowed"); + static_assert(boost::is_lvalue_reference::value, + "binding rvalue references to optional lvalue references is disallowed"); #endif } template BOOST_DEDUCED_TYPENAME boost::remove_reference::type& forward_reference(T&& r) { - BOOST_STATIC_ASSERT_MSG(boost::is_lvalue_reference::value, - "binding rvalue references to optional lvalue references is disallowed"); + static_assert(boost::is_lvalue_reference::value, + "binding rvalue references to optional lvalue references is disallowed"); return optional_detail::forward(r); } @@ -68,8 +68,8 @@ void prevent_assignment_from_false_const_integral() #ifdef BOOST_OPTIONAL_CONFIG_NO_PROPER_ASSIGN_FROM_CONST_INT // MSVC compiler without rvalue references: we need to disable the assignment from // const integral lvalue reference, as it may be an invalid temporary - BOOST_STATIC_ASSERT_MSG(!is_const_integral::value, - "binding const lvalue references to integral types is disabled in this compiler"); + static_assert(!is_const_integral::value, + "binding const lvalue references to integral types is disabled in this compiler"); #endif #endif } diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index afe42da8..0fd44480 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -1053,7 +1053,7 @@ class optional template class optional { - BOOST_STATIC_ASSERT_MSG(sizeof(T) == 0, "Optional rvalue references are illegal."); + static_assert(sizeof(T) == 0, "Optional rvalue references are illegal."); } ; } // namespace boost @@ -1163,7 +1163,7 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, optional_detail::optional_tag const&) { - BOOST_STATIC_ASSERT_MSG(sizeof(CharType) == 0, "If you want to output boost::optional, include header "); + static_assert(sizeof(CharType) == 0, "If you want to output boost::optional, include header "); return os; } diff --git a/test/optional_test_convert_assign.cpp b/test/optional_test_convert_assign.cpp index 5e1cedac..a0675206 100644 --- a/test/optional_test_convert_assign.cpp +++ b/test/optional_test_convert_assign.cpp @@ -40,9 +40,9 @@ void test_no_bad_assignment() { #if !defined(BOOST_NO_CXX11_DECLTYPE) && !BOOST_WORKAROUND(BOOST_MSVC, < 1800) // this means that type trait `boost::is_assignable` works. - BOOST_STATIC_ASSERT((boost::is_assignable&, bool>::value)); - BOOST_STATIC_ASSERT((boost::is_assignable&, implicit_bool_conv>::value)); - BOOST_STATIC_ASSERT((! boost::is_assignable&, explicit_bool_conv>::value)); + static_assert((boost::is_assignable&, bool>::value), "ERROR"); + static_assert((boost::is_assignable&, implicit_bool_conv>::value), "ERROR"); + static_assert((! boost::is_assignable&, explicit_bool_conv>::value), "ERROR"); #endif } diff --git a/test/optional_test_convert_from_T.cpp b/test/optional_test_convert_from_T.cpp index bfa1a0aa..60f94cc7 100644 --- a/test/optional_test_convert_from_T.cpp +++ b/test/optional_test_convert_from_T.cpp @@ -28,14 +28,14 @@ struct superconv { #ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES template - superconv(T&&) { BOOST_STATIC_ASSERT(sizeof(T) == 0); } + superconv(T&&) { static_assert(sizeof(T) == 0, "ERROR"); } #else template - superconv(const T&) { BOOST_STATIC_ASSERT(sizeof(T) == 0); } + superconv(const T&) { static_assert(sizeof(T) == 0, "ERROR"); } template - superconv( T&) { BOOST_STATIC_ASSERT(sizeof(T) == 0); } + superconv( T&) { static_assert(sizeof(T) == 0, "ERROR"); } #endif - + superconv() {} }; @@ -52,19 +52,19 @@ void test_optional_optional_T() { optional oi1 (1), oiN; optional< optional > ooi1 (oi1), ooiN(oiN); - + BOOST_TEST(ooi1); BOOST_TEST(*ooi1); BOOST_TEST_EQ(**ooi1, 1); - + BOOST_TEST(ooiN); BOOST_TEST(!*ooiN); -} +} int main() { test_optional_optional_T(); test_optional_of_superconverting_T(); - + return boost::report_errors(); } diff --git a/test/optional_test_noexcept_move.cpp b/test/optional_test_noexcept_move.cpp index bc4fa97a..6ff8a9a9 100644 --- a/test/optional_test_noexcept_move.cpp +++ b/test/optional_test_noexcept_move.cpp @@ -44,36 +44,36 @@ struct NothrowNone { #if 0 // these also test type_traits, which are wrong void test_noexcept_as_defined() // this is a compile-time test { - BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible::value); - BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable::value); - - BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible::value); - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable::value); - - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible::value); - BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable::value); - - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible::value); - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable::value); + static_assert(::boost::is_nothrow_move_constructible::value, "ERROR"); + static_assert(::boost::is_nothrow_move_assignable::value, "ERROR"); + + static_assert(::boost::is_nothrow_move_constructible::value, "ERROR"); + static_assert(!::boost::is_nothrow_move_assignable::value, "ERROR"); + + static_assert(!::boost::is_nothrow_move_constructible::value, "ERROR"); + static_assert(::boost::is_nothrow_move_assignable::value, "ERROR"); + + static_assert(!::boost::is_nothrow_move_constructible::value, "ERROR"); + static_assert(!::boost::is_nothrow_move_assignable::value, "ERROR"); } void test_noexcept_on_optional_with_type_traits() // this is a compile-time test { - BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible >::value); - BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable >::value); - BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); - - BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible >::value); - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); - BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); - - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible >::value); - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); - BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); - - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible >::value); - BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); - BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); + static_assert(::boost::is_nothrow_move_constructible >::value, "ERROR"); + static_assert(::boost::is_nothrow_move_assignable >::value, "ERROR"); + static_assert(BOOST_NOEXCEPT_EXPR(optional()), "ERROR"); + + static_assert(::boost::is_nothrow_move_constructible >::value, "ERROR"); + static_assert(!::boost::is_nothrow_move_assignable >::value, "ERROR"); + static_assert(BOOST_NOEXCEPT_EXPR(optional()), "ERROR"); + + static_assert(!::boost::is_nothrow_move_constructible >::value, "ERROR"); + static_assert(!::boost::is_nothrow_move_assignable >::value, "ERROR"); + static_assert(BOOST_NOEXCEPT_EXPR(optional()), "ERROR"); + + static_assert(!::boost::is_nothrow_move_constructible >::value, "ERROR"); + static_assert(!::boost::is_nothrow_move_assignable >::value, "ERROR"); + static_assert(BOOST_NOEXCEPT_EXPR(optional()), "ERROR"); } #endif @@ -87,22 +87,22 @@ void test_noexcept_optional_with_operator() // compile-time test ONxC onxC; ONxA onxA; ONx0 onx0; - - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx2() )); - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx2(std::move(onx2)) )); - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( onx2 = ONx2() )); - - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxC() )); - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxC(std::move(onxC)) )); - BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onxC = ONxC() )); - - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxA() )); - BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( ONxA(std::move(onxA)) )); - BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onxA = ONxA() )); - - BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx0() )); - BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( ONx0(std::move(onx0)) )); - BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onx0 = ONx0() )); + + static_assert( BOOST_NOEXCEPT_EXPR( ONx2() ), "ERROR"); + static_assert( BOOST_NOEXCEPT_EXPR( ONx2(std::move(onx2)) ), "ERROR"); + static_assert( BOOST_NOEXCEPT_EXPR( onx2 = ONx2() ), "ERROR"); + + static_assert( BOOST_NOEXCEPT_EXPR( ONxC() ), "ERROR"); + static_assert( BOOST_NOEXCEPT_EXPR( ONxC(std::move(onxC)) ), "ERROR"); + static_assert(!BOOST_NOEXCEPT_EXPR( onxC = ONxC() ), "ERROR"); + + static_assert( BOOST_NOEXCEPT_EXPR( ONxA() ), "ERROR"); + static_assert(!BOOST_NOEXCEPT_EXPR( ONxA(std::move(onxA)) ), "ERROR"); + static_assert(!BOOST_NOEXCEPT_EXPR( onxA = ONxA() ), "ERROR"); + + static_assert( BOOST_NOEXCEPT_EXPR( ONx0() ), "ERROR"); + static_assert(!BOOST_NOEXCEPT_EXPR( ONx0(std::move(onx0)) ), "ERROR"); + static_assert(!BOOST_NOEXCEPT_EXPR( onx0 = ONx0() ), "ERROR"); } #endif // !defined BOOST_NO_CXX11_NOEXCEPT @@ -112,5 +112,3 @@ int main() { return 0; } - - diff --git a/test/optional_test_path_assignment.cpp b/test/optional_test_path_assignment.cpp index 485e741d..d8240d14 100644 --- a/test/optional_test_path_assignment.cpp +++ b/test/optional_test_path_assignment.cpp @@ -66,7 +66,7 @@ int main() optFs1 = optFs2; // the following still fails although it shouldn't - //BOOST_STATIC_ASSERT((std::is_copy_constructible>::value)); + //static_assert((std::is_copy_constructible>::value), "ERROR"); #endif #endif diff --git a/test/optional_test_ref_convert_assign_const_int_prevented.cpp b/test/optional_test_ref_convert_assign_const_int_prevented.cpp index e692475f..153e6a8c 100644 --- a/test/optional_test_ref_convert_assign_const_int_prevented.cpp +++ b/test/optional_test_ref_convert_assign_const_int_prevented.cpp @@ -26,7 +26,7 @@ int main() #ifdef BOOST_OPTIONAL_CONFIG_NO_PROPER_ASSIGN_FROM_CONST_INT test_converting_assignment(); #else - BOOST_STATIC_ASSERT_MSG(false, "EXPECTED TEST COMPILE-TIME FAILURE"); + static_assert(false, "EXPECTED TEST COMPILE-TIME FAILURE"); #endif return boost::report_errors(); } diff --git a/test/optional_test_sfinae_friendly_ctor.cpp b/test/optional_test_sfinae_friendly_ctor.cpp index c13bb2bb..2eff2ff7 100644 --- a/test/optional_test_sfinae_friendly_ctor.cpp +++ b/test/optional_test_sfinae_friendly_ctor.cpp @@ -21,29 +21,29 @@ using boost::optional; struct X {}; struct Y {}; - + struct Resource { explicit Resource(const X&) {} }; -BOOST_STATIC_ASSERT(( boost::is_constructible::value )); -BOOST_STATIC_ASSERT(( !boost::is_constructible::value )); +static_assert(( boost::is_constructible::value ), "ERROR"); +static_assert(( !boost::is_constructible::value ), "ERROR"); -BOOST_STATIC_ASSERT(( boost::is_constructible, const X&>::value )); -BOOST_STATIC_ASSERT(( !boost::is_constructible, const Y&>::value )); +static_assert(( boost::is_constructible, const X&>::value ), "ERROR"); +static_assert(( !boost::is_constructible, const Y&>::value ), "ERROR"); #ifndef BOOST_OPTIONAL_DETAIL_NO_SFINAE_FRIENDLY_CONSTRUCTORS -BOOST_STATIC_ASSERT(( boost::is_constructible< optional< optional >, optional >::value )); -BOOST_STATIC_ASSERT(( !boost::is_constructible< optional, optional< optional > >::value )); +static_assert(( boost::is_constructible< optional< optional >, optional >::value ), "ERROR"); +static_assert(( !boost::is_constructible< optional, optional< optional > >::value ), "ERROR"); -BOOST_STATIC_ASSERT(( boost::is_constructible< optional< optional >, const optional& >::value )); -BOOST_STATIC_ASSERT(( !boost::is_constructible< optional, const optional< optional >& >::value )); +static_assert(( boost::is_constructible< optional< optional >, const optional& >::value ), "ERROR"); +static_assert(( !boost::is_constructible< optional, const optional< optional >& >::value ), "ERROR"); -BOOST_STATIC_ASSERT(( boost::is_constructible, const optional&>::value )); -BOOST_STATIC_ASSERT(( !boost::is_constructible, const optional&>::value )); +static_assert(( boost::is_constructible, const optional&>::value ), "ERROR"); +static_assert(( !boost::is_constructible, const optional&>::value ), "ERROR"); #endif - + #endif int main() { } From 5e7fdf5ec5eca7b562dd1a56409f49e7ed2d1896 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Wed, 16 Oct 2024 23:42:56 +0200 Subject: [PATCH 17/23] Clean remnants of StaticAssert and mpl --- include/boost/optional/detail/optional_utility.hpp | 2 -- include/boost/optional/optional.hpp | 1 - test/CMakeLists.txt | 2 +- test/Jamfile.v2 | 4 ++-- test/optional_test.cpp | 2 -- test/optional_test_noexcept_move.cpp | 1 - 6 files changed, 3 insertions(+), 9 deletions(-) diff --git a/include/boost/optional/detail/optional_utility.hpp b/include/boost/optional/detail/optional_utility.hpp index 581a7e7c..f7c04ffe 100644 --- a/include/boost/optional/detail/optional_utility.hpp +++ b/include/boost/optional/detail/optional_utility.hpp @@ -25,9 +25,7 @@ inline constexpr T&& forward(typename boost::remove_reference::type& t) noexc template inline constexpr T&& forward(typename boost::remove_reference::type&& t) noexcept { -#ifndef BOOST_NO_CXX11_STATIC_ASSERT static_assert(!boost::is_lvalue_reference::value, "Can not forward an rvalue as an lvalue."); -#endif return static_cast(t); } diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 0fd44480..b8f402d8 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eedb6f8c..9a030809 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,6 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST) if(HAVE_BOOST_TEST) -boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::mpl Boost::tuple) +boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::tuple) endif() diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 33c3f25f..21ea8d38 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -26,14 +26,14 @@ project cxx11_defaulted_moves cxx11_deleted_functions cxx11_explicit_conversion_operators - cxx11_noexcept +# cxx11_noexcept cxx11_rvalue_references cxx11_variadic_templates ] ; -run optional_test.cpp : : : /boost/bind//boost_bind /boost/mpl//boost_mpl ; +run optional_test.cpp : : : /boost/bind//boost_bind ; run optional_test_assign.cpp ; run optional_test_swap.cpp ; compile optional_test_wuninitialized.cpp ; diff --git a/test/optional_test.cpp b/test/optional_test.cpp index bd8f90fc..b7d96039 100644 --- a/test/optional_test.cpp +++ b/test/optional_test.cpp @@ -24,8 +24,6 @@ #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #include "boost/bind/apply.hpp" // Included just to test proper interaction with boost::apply<> as reported by Daniel Wallin #endif -#include "boost/mpl/bool.hpp" -#include "boost/mpl/bool_fwd.hpp" // For mpl::true_ and mpl::false_ #include "boost/optional/optional.hpp" diff --git a/test/optional_test_noexcept_move.cpp b/test/optional_test_noexcept_move.cpp index 6ff8a9a9..3aba93ae 100644 --- a/test/optional_test_noexcept_move.cpp +++ b/test/optional_test_noexcept_move.cpp @@ -9,7 +9,6 @@ // You are welcome to contact the author at: // akrzemi1@gmail.com -#include "boost/static_assert.hpp" #include "boost/optional/optional.hpp" #ifdef BOOST_BORLANDC From 86f3288312d9e5f24699ce4e15afec041ea3caf4 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Thu, 17 Oct 2024 00:21:26 +0200 Subject: [PATCH 18/23] fix swap tests --- test/optional_test_inplace_fail.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/optional_test_inplace_fail.cpp b/test/optional_test_inplace_fail.cpp index 38722baf..c66effd1 100644 --- a/test/optional_test_inplace_fail.cpp +++ b/test/optional_test_inplace_fail.cpp @@ -31,10 +31,10 @@ struct Guard std::string str; Guard() : num() {} Guard(double num_, std::string str_) : num(num_), str(str_) {} - + friend bool operator==(const Guard& lhs, const Guard& rhs) { return lhs.num == rhs.num && lhs.str == rhs.str; } friend bool operator!=(const Guard& lhs, const Guard& rhs) { return !(lhs == rhs); } - + private: Guard(const Guard&); Guard& operator=(const Guard&); @@ -50,4 +50,4 @@ int main() NOTHING_TO_TEST_SO_JUST_FAIL #endif return 0; -} \ No newline at end of file +} From b8fd0bcb5f1547f4ab22d5e6fc694b60e1af5af4 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Thu, 17 Oct 2024 07:15:01 +0200 Subject: [PATCH 19/23] fix swap tests --- test/optional_test_swap.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/optional_test_swap.cpp b/test/optional_test_swap.cpp index d997ef6c..bdd629a9 100644 --- a/test/optional_test_swap.cpp +++ b/test/optional_test_swap.cpp @@ -15,7 +15,6 @@ // #include "boost/optional/optional.hpp" -#include "boost/utility/in_place_factory.hpp" #ifdef BOOST_BORLANDC #pragma hdrstop @@ -189,9 +188,9 @@ namespace optional_swap_test return; if( !hasX ) - x = boost::in_place('\0'); + x.emplace('\0'); else if ( !hasY ) - y = boost::in_place('\0'); + y.emplace('\0'); optional_swap_test::swap(*x,*y); From eab3caf7edbe2e9876b7fd5a9dcb61ec05823ee8 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Thu, 17 Oct 2024 18:47:18 +0200 Subject: [PATCH 20/23] indicate that tests depend on utility --- test/CMakeLists.txt | 2 +- test/Jamfile.v2 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9a030809..e3df6bb8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,6 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST) if(HAVE_BOOST_TEST) -boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::tuple) +boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::tuple Boost::utility) endif() diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 21ea8d38..94e48801 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -56,7 +56,7 @@ run optional_test_ref_convert_assign_const_int.cpp ; run optional_test_ref_portable_minimum.cpp ; run optional_test_ref_move.cpp ; run optional_test_ref_to_val.cpp ; -run optional_test_inplace_factory.cpp ; +run optional_test_inplace_factory.cpp : : : /boost/utility//in_place_factories ; run optional_test_io.cpp ; run optional_test_move.cpp ; run optional_test_noexcept_move.cpp ; @@ -93,7 +93,7 @@ compile-fail optional_test_fail_io_without_io.cpp ; compile-fail optional_test_fail_none_io_without_io.cpp ; compile-fail optional_test_fail_convert_assign_of_enums.cpp ; run optional_test_static_properties.cpp ; -compile optional_test_maybe_uninitialized_warning.cpp ; +compile optional_test_maybe_uninitialized_warning.cpp ; : : : /boost/bind//boost_bind compile optional_test_deleted_default_ctor.cpp ; compile optional_test_constructible_from_other.cpp ; #run optional_xconfig_HACK_TO_LIST_PREDEFINED_MACROS.cpp ; From d5a0e294a7b9ffca4cef78618f80e04855238ad2 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Thu, 17 Oct 2024 19:00:04 +0200 Subject: [PATCH 21/23] Revert "indicate that tests depend on utility" This reverts commit eab3caf7edbe2e9876b7fd5a9dcb61ec05823ee8. --- test/CMakeLists.txt | 2 +- test/Jamfile.v2 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e3df6bb8..9a030809 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,6 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST) if(HAVE_BOOST_TEST) -boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::tuple Boost::utility) +boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::tuple) endif() diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 94e48801..21ea8d38 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -56,7 +56,7 @@ run optional_test_ref_convert_assign_const_int.cpp ; run optional_test_ref_portable_minimum.cpp ; run optional_test_ref_move.cpp ; run optional_test_ref_to_val.cpp ; -run optional_test_inplace_factory.cpp : : : /boost/utility//in_place_factories ; +run optional_test_inplace_factory.cpp ; run optional_test_io.cpp ; run optional_test_move.cpp ; run optional_test_noexcept_move.cpp ; @@ -93,7 +93,7 @@ compile-fail optional_test_fail_io_without_io.cpp ; compile-fail optional_test_fail_none_io_without_io.cpp ; compile-fail optional_test_fail_convert_assign_of_enums.cpp ; run optional_test_static_properties.cpp ; -compile optional_test_maybe_uninitialized_warning.cpp ; : : : /boost/bind//boost_bind +compile optional_test_maybe_uninitialized_warning.cpp ; compile optional_test_deleted_default_ctor.cpp ; compile optional_test_constructible_from_other.cpp ; #run optional_xconfig_HACK_TO_LIST_PREDEFINED_MACROS.cpp ; From 5ad8b01512a9ad14f49eaecd49f90a5b79e7263e Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Thu, 17 Oct 2024 19:22:21 +0200 Subject: [PATCH 22/23] add cmake dependency on Utils --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9a030809..e3df6bb8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,6 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST) if(HAVE_BOOST_TEST) -boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::tuple) +boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::tuple Boost::utility) endif() From c716bc552b688bc1030663fd15f2ddf53067612b Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Thu, 17 Oct 2024 20:26:17 +0200 Subject: [PATCH 23/23] add dep on static_assert to the jam file --- test/Jamfile.v2 | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 21ea8d38..af8e8310 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -28,6 +28,7 @@ project cxx11_explicit_conversion_operators # cxx11_noexcept cxx11_rvalue_references + cxx11_static_assert cxx11_variadic_templates ] ;