Fix for clang, when adapted type is convertible from other types.

The problem was with copy/move constructors of boost::optional<T>, which invoked
optional_base<T> 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<T>, 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.
This commit is contained in:
Andrey Semashev
2018-11-04 17:35:10 +03:00
parent 69e239530e
commit 24d29e5865
2 changed files with 8 additions and 6 deletions

View File

@ -30,7 +30,7 @@ class tc_optional_base : public optional_tag
: :
m_initialized(false) {} m_initialized(false) {}
tc_optional_base ( argument_type val ) tc_optional_base ( init_value_tag, argument_type val )
: :
m_initialized(true), m_storage(val) {} m_initialized(true), m_storage(val) {}

View File

@ -107,6 +107,8 @@ using optional_ns::in_place_init_if;
namespace optional_detail { namespace optional_detail {
struct init_value_tag {};
struct optional_tag {}; struct optional_tag {};
@ -147,7 +149,7 @@ class optional_base : public optional_tag
// Creates an optional<T> initialized with 'val'. // Creates an optional<T> initialized with 'val'.
// Can throw if T::T(T const&) does // Can throw if T::T(T const&) does
optional_base ( argument_type val ) optional_base ( init_value_tag, argument_type val )
: :
m_initialized(false) m_initialized(false)
{ {
@ -157,7 +159,7 @@ class optional_base : public optional_tag
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES #ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
// move-construct an optional<T> initialized from an rvalue-ref to 'val'. // move-construct an optional<T> initialized from an rvalue-ref to 'val'.
// Can throw if T::T(T&&) does // 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) m_initialized(false)
{ {
@ -870,12 +872,12 @@ class optional
// Creates an optional<T> initialized with 'val'. // Creates an optional<T> initialized with 'val'.
// Can throw if T::T(T const&) does // 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 #ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
// Creates an optional<T> initialized with 'move(val)'. // Creates an optional<T> initialized with 'move(val)'.
// Can throw if T::T(T &&) does // Can throw if T::T(T &&) does
optional ( rval_reference_type val ) : base( boost::forward<T>(val) ) optional ( rval_reference_type val ) : base(optional_detail::init_value_tag(), boost::forward<T>(val))
{} {}
#endif #endif