diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 743479a5378..c8c46565210 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,15 @@ +2018-03-14 Jonathan Wakely + + PR libstdc++/78420 + * include/bits/stl_function.h (greater<_Tp*>, less<_Tp*>) + (greater_equal<_Tp*>, less_equal<_Tp>*): Add partial specializations + to ensure total order for pointers. + (greater, less, greater_equal, less_equal): + Add operator() overloads for pointer arguments and make generic + overloads dispatch to new _S_cmp functions when comparisons would + use built-in operators for pointers. + * testsuite/20_util/function_objects/comparisons_pointer.cc: New. + 2018-03-12 Jonathan Wakely PR libstdc++/84773 diff --git a/libstdc++-v3/include/bits/stl_function.h b/libstdc++-v3/include/bits/stl_function.h index f5f98b25395..0affaf7da3a 100644 --- a/libstdc++-v3/include/bits/stl_function.h +++ b/libstdc++-v3/include/bits/stl_function.h @@ -406,14 +406,65 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __x <= __y; } }; -#if __cplusplus > 201103L + // Partial specialization of std::greater for pointers. + template + struct greater<_Tp*> : public binary_function<_Tp*, _Tp*, bool> + { + _GLIBCXX14_CONSTEXPR bool + operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW + { + if (__builtin_constant_p (__x > __y)) + return __x > __y; + return (__UINTPTR_TYPE__)__x > (__UINTPTR_TYPE__)__y; + } + }; + + // Partial specialization of std::less for pointers. + template + struct less<_Tp*> : public binary_function<_Tp*, _Tp*, bool> + { + _GLIBCXX14_CONSTEXPR bool + operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW + { + if (__builtin_constant_p (__x < __y)) + return __x < __y; + return (__UINTPTR_TYPE__)__x < (__UINTPTR_TYPE__)__y; + } + }; + + // Partial specialization of std::greater_equal for pointers. + template + struct greater_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool> + { + _GLIBCXX14_CONSTEXPR bool + operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW + { + if (__builtin_constant_p (__x >= __y)) + return __x >= __y; + return (__UINTPTR_TYPE__)__x >= (__UINTPTR_TYPE__)__y; + } + }; + + // Partial specialization of std::less_equal for pointers. + template + struct less_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool> + { + _GLIBCXX14_CONSTEXPR bool + operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW + { + if (__builtin_constant_p (__x <= __y)) + return __x <= __y; + return (__UINTPTR_TYPE__)__x <= (__UINTPTR_TYPE__)__y; + } + }; + +#if __cplusplus >= 201402L /// One of the @link comparison_functors comparison functors@endlink. template<> struct equal_to { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u)) @@ -427,8 +478,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct not_equal_to { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) != std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) != std::forward<_Up>(__u)) @@ -442,14 +492,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct greater { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) > std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) > std::forward<_Up>(__u)) - { return std::forward<_Tp>(__t) > std::forward<_Up>(__u); } + { + return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), + __ptr_cmp<_Tp, _Up>{}); + } + + template + constexpr bool + operator()(_Tp* __t, _Up* __u) const noexcept + { return greater>{}(__t, __u); } typedef __is_transparent is_transparent; + + private: + template + static constexpr decltype(auto) + _S_cmp(_Tp&& __t, _Up&& __u, false_type) + { return std::forward<_Tp>(__t) > std::forward<_Up>(__u); } + + template + static constexpr bool + _S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept + { + return greater{}( + static_cast(std::forward<_Tp>(__t)), + static_cast(std::forward<_Up>(__u))); + } + + template + struct __not_overloaded; + + // False if we can call operator>(T,U) + template + struct __not_overloaded<_Tp, _Up, __void_t< + decltype(operator>(std::declval<_Tp>(), std::declval<_Up>()))>> + : false_type { }; + + template + struct __not_overloaded2 : true_type { }; + + // False if we can call T.operator>(U) + template + struct __not_overloaded2<_Tp, _Up, __void_t< + decltype(std::declval<_Tp>().operator>(std::declval<_Up>()))>> + : false_type { }; + + template + struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { }; + + template + using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>, + is_convertible<_Tp, const volatile void*>, + is_convertible<_Up, const volatile void*>>; }; /// One of the @link comparison_functors comparison functors@endlink. @@ -457,14 +555,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct less { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) < std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) < std::forward<_Up>(__u)) - { return std::forward<_Tp>(__t) < std::forward<_Up>(__u); } + { + return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), + __ptr_cmp<_Tp, _Up>{}); + } + + template + constexpr bool + operator()(_Tp* __t, _Up* __u) const noexcept + { return less>{}(__t, __u); } typedef __is_transparent is_transparent; + + private: + template + static constexpr decltype(auto) + _S_cmp(_Tp&& __t, _Up&& __u, false_type) + { return std::forward<_Tp>(__t) < std::forward<_Up>(__u); } + + template + static constexpr bool + _S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept + { + return less{}( + static_cast(std::forward<_Tp>(__t)), + static_cast(std::forward<_Up>(__u))); + } + + template + struct __not_overloaded; + + // False if we can call operator<(T,U) + template + struct __not_overloaded<_Tp, _Up, __void_t< + decltype(operator<(std::declval<_Tp>(), std::declval<_Up>()))>> + : false_type { }; + + template + struct __not_overloaded2 : true_type { }; + + // False if we can call T.operator<(U) + template + struct __not_overloaded2<_Tp, _Up, __void_t< + decltype(std::declval<_Tp>().operator<(std::declval<_Up>()))>> + : false_type { }; + + template + struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { }; + + template + using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>, + is_convertible<_Tp, const volatile void*>, + is_convertible<_Up, const volatile void*>>; }; /// One of the @link comparison_functors comparison functors@endlink. @@ -472,14 +618,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct greater_equal { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) >= std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) >= std::forward<_Up>(__u)) - { return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); } + { + return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), + __ptr_cmp<_Tp, _Up>{}); + } + + template + constexpr bool + operator()(_Tp* __t, _Up* __u) const noexcept + { return greater_equal>{}(__t, __u); } typedef __is_transparent is_transparent; + + private: + template + static constexpr decltype(auto) + _S_cmp(_Tp&& __t, _Up&& __u, false_type) + { return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); } + + template + static constexpr bool + _S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept + { + return greater_equal{}( + static_cast(std::forward<_Tp>(__t)), + static_cast(std::forward<_Up>(__u))); + } + + template + struct __not_overloaded; + + // False if we can call operator>=(T,U) + template + struct __not_overloaded<_Tp, _Up, __void_t< + decltype(operator>=(std::declval<_Tp>(), std::declval<_Up>()))>> + : false_type { }; + + template + struct __not_overloaded2 : true_type { }; + + // False if we can call T.operator>=(U) + template + struct __not_overloaded2<_Tp, _Up, __void_t< + decltype(std::declval<_Tp>().operator>=(std::declval<_Up>()))>> + : false_type { }; + + template + struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { }; + + template + using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>, + is_convertible<_Tp, const volatile void*>, + is_convertible<_Up, const volatile void*>>; }; /// One of the @link comparison_functors comparison functors@endlink. @@ -487,16 +681,64 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct less_equal { template - _GLIBCXX14_CONSTEXPR - auto + constexpr auto operator()(_Tp&& __t, _Up&& __u) const noexcept(noexcept(std::forward<_Tp>(__t) <= std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) <= std::forward<_Up>(__u)) - { return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); } + { + return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), + __ptr_cmp<_Tp, _Up>{}); + } + + template + constexpr bool + operator()(_Tp* __t, _Up* __u) const noexcept + { return less_equal>{}(__t, __u); } typedef __is_transparent is_transparent; + + private: + template + static constexpr decltype(auto) + _S_cmp(_Tp&& __t, _Up&& __u, false_type) + { return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); } + + template + static constexpr bool + _S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept + { + return less_equal{}( + static_cast(std::forward<_Tp>(__t)), + static_cast(std::forward<_Up>(__u))); + } + + template + struct __not_overloaded; + + // False if we can call operator<=(T,U) + template + struct __not_overloaded<_Tp, _Up, __void_t< + decltype(operator<=(std::declval<_Tp>(), std::declval<_Up>()))>> + : false_type { }; + + template + struct __not_overloaded2 : true_type { }; + + // False if we can call T.operator<=(U) + template + struct __not_overloaded2<_Tp, _Up, __void_t< + decltype(std::declval<_Tp>().operator<=(std::declval<_Up>()))>> + : false_type { }; + + template + struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { }; + + template + using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>, + is_convertible<_Tp, const volatile void*>, + is_convertible<_Up, const volatile void*>>; }; -#endif +#endif // C++14 /** @} */ // 20.3.4 logical operations diff --git a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc new file mode 100644 index 00000000000..6b2b8be0f51 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc @@ -0,0 +1,206 @@ +// Copyright (C) 2018 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-do run } + +#include +#include +#include + +int b[8]; +int a[8]; + +void +test01() +{ + auto p = a + 8; + std::greater gt; + + std::stringstream ss; + ss << gt(p, b) << ' ' << gt(b, p) << ' ' << (!gt(p, b) && !gt(b, p)); + int sum = 0, n = 0; + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( gt(a+1, a), "constexpr greater" ); + static_assert( !gt(a, a+1), "constexpr greater" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = a + 8; + std::greater<> gt2; + ss << gt2(p2, b) << ' ' << gt2(b, p2) << ' ' << (!gt2(p2, b) && !gt2(b, p2)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( gt2(a+1, a), "constexpr greater<>" ); + static_assert( !gt2(a, a+1), "constexpr greater<>" ); +#endif +} + +void +test02() +{ + auto p = a + 8; + std::less lt; + + std::stringstream ss; + ss << lt(p, b) << ' ' << lt(b, p) << ' ' << (!lt(p, b) && !lt(b, p)); + int sum = 0, n = 0; + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( lt(a, a+1), "constexpr less" ); + static_assert( !lt(a+1, a), "constexpr less" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = a + 8; + std::less<> lt2; + ss << lt2(p2, b) << ' ' << lt2(b, p2) << ' ' << (!lt2(p2, b) && !lt2(b, p2)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( lt2(a, a+1), "constexpr less<>" ); + static_assert( !lt2(a+1, a), "constexpr less<>" ); +#endif +} + +void +test03() +{ + auto p = a + 8; + std::greater_equal ge; + + std::stringstream ss; + ss << !ge(p, b) << ' ' << !ge(b, p) << ' ' << (ge(p, b) && ge(b, p)); + int sum = 0, n = 0; + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( !ge(a, a+1), "constexpr greater_equal" ); + static_assert( ge(a, a), "constexpr greater_equal" ); + static_assert( ge(a+1, a), "constexpr greater_equal" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = a + 8; + std::greater_equal<> ge2; + ss << !ge2(p2, b) << ' ' << !ge2(b, p2) << ' ' << (ge2(p2, b) && ge2(b, p2)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( !ge2(a, a+1), "constexpr greater_equal<>" ); + static_assert( ge2(a, a), "constexpr greater_equal<>" ); + static_assert( ge2(a+1, a), "constexpr greater_equal<>" ); +#endif +} + +void +test04() +{ + auto p = a + 8; + std::less_equal le; + + std::stringstream ss; + ss << !le(p, b) << ' ' << !le(b, p) << ' ' << (le(p, b) && le(b, p)); + int sum = 0, n = 0; + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( !le(a+1, a), "constexpr less_equal" ); + static_assert( le(a, a), "constexpr less_equal" ); + static_assert( le(a, a+1), "constexpr less_equal" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = a + 8; + std::less_equal<> le2; + ss << !le2(p2, b) << ' ' << !le2(b, p2) << ' ' << (le2(p2, b) && le2(b, p2)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( !le2(a+1, a), "constexpr less_equal<>" ); + static_assert( le2(a, a), "constexpr less_equal<>" ); + static_assert( le2(a, a+1), "constexpr less_equal<>" ); +#endif +} + +struct X { + operator const X*() const { return this; } +}; + +X x; +X y[4]; + +void +test05() +{ + std::less lt; + auto p = y + 4; + std::stringstream ss; + ss << lt(x, p) << ' ' << lt(p, x) << ' ' << (!lt(p, x) && !lt(x, p)); + int sum = 0; + int n = 0; + while (ss >> n) + sum += n; + assert( sum == 1 ); + +#if __cplusplus >= 201402L + static_assert( lt(y, y+1), "constexpr less" ); + static_assert( !lt(y+1, y), "constexpr less" ); + + ss.str(""); + ss.clear(); + sum = 0; + auto p2 = y + 4; + std::less<> lt2; + ss << lt2(x, p2) << ' ' << lt2(p2, x) << ' ' << (!lt2(x, p2) && !lt2(p2, x)); + while (ss >> n) + sum += n; + VERIFY( sum == 1 ); + + static_assert( lt2(y, y+1), "constexpr less<>" ); + static_assert( !lt2(y+1, y), "constexpr less<>" ); +#endif +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); +}