diff --git a/libstdc++-v3/include/experimental/propagate_const b/libstdc++-v3/include/experimental/propagate_const index 258ef6fdd44..12b0f27462a 100644 --- a/libstdc++-v3/include/experimental/propagate_const +++ b/libstdc++-v3/include/experimental/propagate_const @@ -50,6 +50,48 @@ namespace experimental { inline namespace fundamentals_v2 { + template + using __propagate_const_elem_type + = remove_reference_t())>; + + template, + bool = is_convertible::value> + struct __propagate_const_conversion_c + { }; + + template + struct __propagate_const_conversion_c<_Tp, _Elem, true> + { + constexpr operator const _Elem*() const; + }; + + template, + bool = is_convertible<_Tp, _Elem*>::value> + struct __propagate_const_conversion_nc + { }; + + template + struct __propagate_const_conversion_nc<_Tp, _Elem, true> + { + constexpr operator _Elem*(); + }; + + // Base class of propagate_const when T is a class type. + template + struct __propagate_const_conversions + : __propagate_const_conversion_c<_Tp>, __propagate_const_conversion_nc<_Tp> + { }; + + // Base class of propagate_const when T is a pointer type. + template + struct __propagate_const_conversions<_Tp*> + { + constexpr operator const _Tp*() const noexcept; + constexpr operator _Tp*() noexcept; + }; + /** * @defgroup propagate_const Const-propagating wrapper * @ingroup libfund-ts @@ -63,10 +105,10 @@ inline namespace fundamentals_v2 /// Const-propagating wrapper. template - class propagate_const + class propagate_const : public __propagate_const_conversions<_Tp> { public: - typedef remove_reference_t())> element_type; + using element_type = __propagate_const_elem_type<_Tp>; private: template @@ -186,16 +228,6 @@ inline namespace fundamentals_v2 return get(); } - template , - is_convertible<_Up, - const element_type*> - >::value, bool>::type = true> - constexpr operator const element_type*() const - { - return get(); - } - constexpr const element_type& operator*() const { return *get(); @@ -212,16 +244,6 @@ inline namespace fundamentals_v2 return get(); } - template , - is_convertible<_Up, - const element_type*> - >::value, bool>::type = true> - constexpr operator element_type*() - { - return get(); - } - constexpr element_type& operator*() { return *get(); @@ -430,6 +452,28 @@ inline namespace fundamentals_v2 return __pt._M_t; } + template + constexpr + __propagate_const_conversions<_Tp*>::operator const _Tp*() const noexcept + { return static_cast*>(this)->get(); } + + template + constexpr + __propagate_const_conversions<_Tp*>::operator _Tp*() noexcept + { return static_cast*>(this)->get(); } + + template + constexpr + __propagate_const_conversion_c<_Tp, _Elem, true>:: + operator const _Elem*() const + { return static_cast*>(this)->get(); } + + template + constexpr + __propagate_const_conversion_nc<_Tp, _Elem, true>:: + operator _Elem*() + { return static_cast*>(this)->get(); } + /// @} group propagate_const } // namespace fundamentals_v2 } // namespace experimental diff --git a/libstdc++-v3/testsuite/experimental/propagate_const/observers/107525.cc b/libstdc++-v3/testsuite/experimental/propagate_const/observers/107525.cc new file mode 100644 index 00000000000..e7ecff73c1a --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/propagate_const/observers/107525.cc @@ -0,0 +1,47 @@ +// { dg-do run { target c++14 } } + +#include +#include + +using std::experimental::propagate_const; + +void +test_base_conversion() +{ + struct Base { }; + struct Derived : Base { }; + + static_assert(std::is_convertible, Base*>::value, + "PR libstdc++/107525 - SFINAE breaks conversion operators"); + static_assert(std::is_convertible, const Base*>::value, + "PR libstdc++/107525 - SFINAE breaks conversion operators"); +} + +void +test_const_conversion() +{ + struct X + { + int* p = nullptr; + + int& operator*() const { return *p; } + int* operator->() const { return p; } + int* get() const { return p; } + + operator int*() { return p; } + operator const int*() const = delete; + }; + + static_assert(!std::is_convertible_v, + "Cannot convert const X to const int*"); + // So should not be able to convert const propagate_const to const int*. + static_assert(!std::is_convertible_v, const int*>, + "So should not be able to convert const propagate_const to " + "const int* (although this is not what LFTSv3 says)"); +} + +int main() +{ + test_base_conversion(); + test_const_conversion(); +}