From 24d29e5865ef51c1ad62d9a98c4f9dd2f25aed22 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sun, 4 Nov 2018 17:35:10 +0300 Subject: [PATCH 1/2] Fix for clang, when adapted type is convertible from other types. The problem was with copy/move constructors of boost::optional, which invoked optional_base constructors with a single argument. Since optional_base has copy/move constructors along with initializing constructors taking a single argument, the latter may be considered by the compiler for viability. While doing so, the compiler may instantiate the template constructor of T with an argument of optional_base, which in turn may fail if the constructor attempts to inspect the type of its argument (e.g. to constrain the set of acceptable types). Specifically, this happens with clang in C++03 mode, when boost::multiprecision::number is wrapped in boost::optional and a copy constructor of boost::optional is invoked. This commit fixes the problem by destinguishing copy/move constructors of optional_base from initializing constructors with an additional tag argument. --- .../detail/optional_trivially_copyable_base.hpp | 2 +- include/boost/optional/optional.hpp | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/boost/optional/detail/optional_trivially_copyable_base.hpp b/include/boost/optional/detail/optional_trivially_copyable_base.hpp index 0734f62..5a37eac 100644 --- a/include/boost/optional/detail/optional_trivially_copyable_base.hpp +++ b/include/boost/optional/detail/optional_trivially_copyable_base.hpp @@ -30,7 +30,7 @@ class tc_optional_base : public optional_tag : m_initialized(false) {} - tc_optional_base ( argument_type val ) + tc_optional_base ( init_value_tag, argument_type val ) : m_initialized(true), m_storage(val) {} diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 104bca4..90acd40 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -107,7 +107,9 @@ using optional_ns::in_place_init_if; namespace optional_detail { -struct optional_tag {} ; +struct init_value_tag {}; + +struct optional_tag {}; template @@ -147,7 +149,7 @@ class optional_base : public optional_tag // Creates an optional initialized with 'val'. // Can throw if T::T(T const&) does - optional_base ( argument_type val ) + optional_base ( init_value_tag, argument_type val ) : m_initialized(false) { @@ -157,7 +159,7 @@ class optional_base : public optional_tag #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 ( rval_reference_type val ) + optional_base ( init_value_tag, rval_reference_type val ) : m_initialized(false) { @@ -870,12 +872,12 @@ class optional // Creates an optional initialized with 'val'. // Can throw if T::T(T const&) does - optional ( argument_type val ) : base(val) {} + 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( boost::forward(val) ) + optional ( rval_reference_type val ) : base(optional_detail::init_value_tag(), boost::forward(val)) {} #endif From 2f3cf1ca84f916b64567cd92ad36cf588fc057df Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sun, 4 Nov 2018 17:54:57 +0300 Subject: [PATCH 2/2] Added a test to verify that boost::optional copy constructor does not invoke T's template constructors. --- test/Jamfile.v2 | 1 + ...optional_test_constructible_from_other.cpp | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 test/optional_test_constructible_from_other.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index f6912a9..7936ad7 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -76,6 +76,7 @@ import testing ; [ run optional_test_static_properties.cpp ] [ 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 ] [ run optional_xconfig_NO_PROPER_ASSIGN_FROM_CONST_INT_pass.cpp ] [ run-fail optional_xconfig_NO_PROPER_ASSIGN_FROM_CONST_INT_fail.cpp ] diff --git a/test/optional_test_constructible_from_other.cpp b/test/optional_test_constructible_from_other.cpp new file mode 100644 index 0000000..e106b69 --- /dev/null +++ b/test/optional_test_constructible_from_other.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2018 Andrey Semashev +// +// 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) + +// The test verifies that Boost.Optional copy constructors do not attempt to invoke +// the element type initializing constructors from templated arguments + +#include +#include + +struct no_type +{ + char data; +}; + +struct yes_type +{ + char data[2]; +}; + +template< unsigned int Size > +struct size_tag {}; + +template< typename T, typename U > +struct is_constructible +{ + template< typename T1, typename U1 > + static yes_type check_helper(size_tag< sizeof(static_cast< T1 >(U1())) >*); + template< typename T1, typename U1 > + static no_type check_helper(...); + + static const bool value = sizeof(check_helper< T, U >(0)) == sizeof(yes_type); +}; + +template< typename T > +class wrapper +{ +public: + wrapper() {} + wrapper(wrapper const&) {} + template< typename U > + wrapper(U const&, typename boost::enable_if_c< is_constructible< T, U >::value, int >::type = 0) {} +}; + +inline boost::optional< wrapper< int > > foo() +{ + return boost::optional< wrapper< int > >(); +} + +int main() +{ + // Invokes boost::optional copy constructor. Should not invoke wrapper constructor from U. + boost::optional< wrapper< int > > res = foo(); + return 0; +}