From 1fb60cb53bb2b86b2a9739a26a8be2c5e4c9e8fc Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sun, 26 Apr 2026 21:57:08 +0200 Subject: [PATCH] fix bug in copy construction of optional --- doc/92_relnotes.qbk | 4 + .../detail/optional_select_implementation.hpp | 3 +- .../boost/optional/detail/union_optional.hpp | 4 +- test/Jamfile.v2 | 1 + test/optional_test_constructors.cpp | 287 ++++++++++++++++++ 5 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 test/optional_test_constructors.cpp diff --git a/doc/92_relnotes.qbk b/doc/92_relnotes.qbk index 88ff039..e467949 100644 --- a/doc/92_relnotes.qbk +++ b/doc/92_relnotes.qbk @@ -11,6 +11,10 @@ [section:relnotes Release Notes] +[heading Boost Release 1.xx] + +* Fixed regression in the copy-initialization of `optional`. This fixes [@https://github.com/boostorg/optional/issues/146 issue #146]. + [heading Boost Release 1.91] * For compilers with full C++11 support (including "unrestricted unions" and ref-qualifiers) diff --git a/include/boost/optional/detail/optional_select_implementation.hpp b/include/boost/optional/detail/optional_select_implementation.hpp index 8a69eea..53a75f8 100644 --- a/include/boost/optional/detail/optional_select_implementation.hpp +++ b/include/boost/optional/detail/optional_select_implementation.hpp @@ -23,7 +23,8 @@ !defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES) && \ !defined(BOOST_NO_CXX11_UNRESTRICTED_UNION) && \ !defined(BOOST_NO_CXX11_NOEXCEPT) && \ - !defined(BOOST_NO_CXX11_DEFAULTED_MOVES) + !defined(BOOST_NO_CXX11_DEFAULTED_MOVES) && \ + !defined(BOOST_OPTIONAL_CONFIG_DISABLE_UNION_OPTIONAL) # define BOOST_OPTIONAL_USES_UNION_IMPLEMENTATION #endif diff --git a/include/boost/optional/detail/union_optional.hpp b/include/boost/optional/detail/union_optional.hpp index 16e150f..b735d5a 100644 --- a/include/boost/optional/detail/union_optional.hpp +++ b/include/boost/optional/detail/union_optional.hpp @@ -391,7 +391,9 @@ namespace boost { template ), BOOST_OPTIONAL_REQUIRES(!optional_detail::is_typed_in_place_factory), - BOOST_OPTIONAL_REQUIRES(!optional_detail::is_in_place_factory)> + BOOST_OPTIONAL_REQUIRES(!optional_detail::is_in_place_factory), + BOOST_OPTIONAL_REQUIRES(!BOOST_OPTIONAL_IS_TAGGED(optional_detail::optional_tag, U)) + > constexpr explicit optional(U&& v) : storage(optional_ns::in_place_init, optional_detail::forward_(v)) {} diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index f625e58..c17a711 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -40,6 +40,7 @@ compile optional_test_constexpr.cpp ; compile optional_test_wuninitialized.cpp ; compile optional_test_fwd_header.cpp ; run optional_test_conversions_from_U.cpp ; +run optional_test_constructors.cpp ; run optional_test_convert_from_T.cpp ; run optional_test_convert_assign.cpp ; run optional_test_empty_braces.cpp ; diff --git a/test/optional_test_constructors.cpp b/test/optional_test_constructors.cpp new file mode 100644 index 0000000..e3483a6 --- /dev/null +++ b/test/optional_test_constructors.cpp @@ -0,0 +1,287 @@ +// Copyright (C) 2026 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com + +#include "boost/optional/optional.hpp" +#include "boost/core/lightweight_test.hpp" +#include + + +using boost::optional; +using boost::none; + +struct MyBool +{ + bool b; + MyBool (bool b) : b(b) {} + operator bool() const { return b; }; +}; + +struct MyExplicitBool +{ + bool b; + MyExplicitBool (bool b) : b(b) {} + operator bool() const { return b; }; +}; + +struct Any +{ + template + Any(T&&...) {} +}; + +struct JustCopyMoveCtor +{ + JustCopyMoveCtor(JustCopyMoveCtor&&) = default; + JustCopyMoveCtor(JustCopyMoveCtor const&) = default; + JustCopyMoveCtor& operator=(JustCopyMoveCtor&&) = delete; + JustCopyMoveCtor& operator=(JustCopyMoveCtor const&) = delete; + JustCopyMoveCtor() = delete; +}; + +struct JustDefault +{ + JustDefault() = default; + JustDefault(JustDefault&&) = delete; +}; + +template +optional empty_optional() +{ + return optional(); // this is to avoid "the most vexing parse" +} + + +template +void direct_test_ctor_optional_T_from_empty_optional_T() +{ + optional om; + const optional oc; + BOOST_TEST(!om); + BOOST_TEST(!oc); + + { + optional ox = om; + BOOST_TEST(!ox); + + optional oy = oc; + BOOST_TEST(!oy); + + optional oz = empty_optional(); + BOOST_TEST(!oz); + } + + { + optional ox = {om}; + BOOST_TEST(!ox); + + optional oy = {oc}; + BOOST_TEST(!oy); + + optional oz = {empty_optional()}; + BOOST_TEST(!oz); + } + + { + optional ox{om}; + BOOST_TEST(!ox); + + optional oy{oc}; + BOOST_TEST(!oy); + + optional oz{empty_optional()}; + BOOST_TEST(!oz); + } + + { + optional ox(om); + BOOST_TEST(!ox); + + optional oy(oc); + BOOST_TEST(!oy); + + optional oz(empty_optional()); + BOOST_TEST(!oz); + } +} + +template +void test_ctor_optional_T_from_empty_optional_T() +{ + direct_test_ctor_optional_T_from_empty_optional_T(); + direct_test_ctor_optional_T_from_empty_optional_T(); + direct_test_ctor_optional_T_from_empty_optional_T, optional>(); + direct_test_ctor_optional_T_from_empty_optional_T, optional>(); + direct_test_ctor_optional_T_from_empty_optional_T, const optional>(); +} + +template +void direct_test_ctor_optional_T_from_optional_U(U val) +{ + optional om; + const optional oc; + BOOST_TEST(!om); + BOOST_TEST(!oc); + + { + optional ox{om}; + BOOST_TEST(!ox); + + optional oy{oc}; + BOOST_TEST(!oy); + + optional oz{empty_optional()}; + BOOST_TEST(!oz); + } + + { + optional ox(om); + BOOST_TEST(!ox); + + optional oy(oc); + BOOST_TEST(!oy); + + optional oz(empty_optional()); + BOOST_TEST(!oz); + } + + { + optional om {val}; + const optional oc {val}; + + { + optional ox{om}; + BOOST_TEST(ox); + + optional oy{oc}; + BOOST_TEST(oy); + + optional oz{boost::make_optional(val)}; + BOOST_TEST(oz); + } + { + optional ox(om); + BOOST_TEST(ox); + + optional oy(oc); + BOOST_TEST(oy); + + optional oz(boost::make_optional(val)); + BOOST_TEST(oz); + } + } +} + +template +void test_ctor_optional_T_from_optional_U(U val) +{ + direct_test_ctor_optional_T_from_optional_U(val); +} + +template +void direct_test_ctor_optional_T_from_valued_optional_T(T val) +{ + optional om(val); + const optional oc(val); + BOOST_TEST(om); + BOOST_TEST(oc); + + { + optional ox = om; + BOOST_TEST(ox); + + optional oy = oc; + BOOST_TEST(oy); + + optional oz = optional(val); + BOOST_TEST(oz); + } + { + optional ox = {om}; + BOOST_TEST(ox); + + optional oy = {oc}; + BOOST_TEST(oy); + + optional oz = {optional(val)}; + BOOST_TEST(oz); + } +} + +template +void test_ctor_optional_T_from_valued_optional_T(T val) +{ + direct_test_ctor_optional_T_from_valued_optional_T(val); + direct_test_ctor_optional_T_from_valued_optional_T>(val); + direct_test_ctor_optional_T_from_valued_optional_T(val); + direct_test_ctor_optional_T_from_optional_U(val); + direct_test_ctor_optional_T_from_optional_U, optional>(val); +} + +template +void direct_test_inplace_tag_constructor() +{ + { + optional ox (boost::in_place_init); + BOOST_TEST(ox); + } + { + optional ox {boost::in_place_init}; + BOOST_TEST(ox); + } +} + +template +void test_inplace_tag_constructor() +{ + direct_test_inplace_tag_constructor(); + direct_test_inplace_tag_constructor>(); +} + + +int main() +{ + test_ctor_optional_T_from_empty_optional_T(); + test_ctor_optional_T_from_empty_optional_T(); + test_ctor_optional_T_from_empty_optional_T(); + test_ctor_optional_T_from_empty_optional_T(); + test_ctor_optional_T_from_empty_optional_T(); + + test_ctor_optional_T_from_empty_optional_T(); + + test_ctor_optional_T_from_empty_optional_T(); + test_ctor_optional_T_from_empty_optional_T(); + + test_inplace_tag_constructor(); + test_inplace_tag_constructor(); + test_inplace_tag_constructor(); + + test_ctor_optional_T_from_optional_U(1); + test_ctor_optional_T_from_optional_U(1); + test_ctor_optional_T_from_optional_U(MyBool{true}); + test_ctor_optional_T_from_optional_U(MyBool{false}); + test_ctor_optional_T_from_optional_U(true); + test_ctor_optional_T_from_optional_U(false); + test_ctor_optional_T_from_optional_U(MyExplicitBool{true}); + test_ctor_optional_T_from_optional_U(MyExplicitBool{false}); + test_ctor_optional_T_from_optional_U(true); + test_ctor_optional_T_from_optional_U(false); + test_ctor_optional_T_from_optional_U(true); + test_ctor_optional_T_from_optional_U(Any{}); + + test_ctor_optional_T_from_valued_optional_T(1); + + optional ob; + optional oa; + oa = ob; + BOOST_TEST(!oa); + + return boost::report_errors(); +}