diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 4be0249b..4272b17c 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -864,6 +864,78 @@ namespace boost { #endif +//////////////////////////////////////////////////////////////////////////// +// TRAITS TYPE DEDETION MECHANISM +// +// Used to implement traits that use a type if present, or a +// default otherwise. + +#if defined(BOOST_MSVC) && BOOST_MSVC <= 1400 + +#define BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(tname) \ + template struct default_type_##tname \ + { \ + \ + template \ + static choice1::type test(choice1, typename X::tname* = 0); \ + \ + template static choice2::type test(choice2, void* = 0); \ + \ + struct DefaultWrap \ + { \ + typedef Default tname; \ + }; \ + \ + enum \ + { \ + value = (1 == sizeof(test(choose()))) \ + }; \ + \ + typedef typename boost::detail::if_true::BOOST_NESTED_TEMPLATE \ + then::type::tname type; \ + } + +#else + +namespace boost { + namespace unordered { + namespace detail { + template struct sfinae : T2 + { + }; + } + } +} + +#define BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(tname) \ + template struct default_type_##tname \ + { \ + \ + template \ + static typename boost::unordered::detail::sfinae::type test(choice1); \ + \ + template static choice2::type test(choice2); \ + \ + struct DefaultWrap \ + { \ + typedef Default tname; \ + }; \ + \ + enum \ + { \ + value = (1 == sizeof(test(choose()))) \ + }; \ + \ + typedef typename boost::detail::if_true::BOOST_NESTED_TEMPLATE \ + then::type::tname type; \ + } + +#endif + +#define BOOST_UNORDERED_DEFAULT_TYPE(T, tname, arg) \ + typename default_type_##tname::type + //////////////////////////////////////////////////////////////////////////////// // // Allocator traits @@ -943,72 +1015,6 @@ namespace boost { } } -#if defined(BOOST_MSVC) && BOOST_MSVC <= 1400 - -#define BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(tname) \ - template struct default_type_##tname \ - { \ - \ - template \ - static choice1::type test(choice1, typename X::tname* = 0); \ - \ - template static choice2::type test(choice2, void* = 0); \ - \ - struct DefaultWrap \ - { \ - typedef Default tname; \ - }; \ - \ - enum \ - { \ - value = (1 == sizeof(test(choose()))) \ - }; \ - \ - typedef typename boost::detail::if_true::BOOST_NESTED_TEMPLATE \ - then::type::tname type; \ - } - -#else - -namespace boost { - namespace unordered { - namespace detail { - template struct sfinae : T2 - { - }; - } - } -} - -#define BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(tname) \ - template struct default_type_##tname \ - { \ - \ - template \ - static typename boost::unordered::detail::sfinae::type test(choice1); \ - \ - template static choice2::type test(choice2); \ - \ - struct DefaultWrap \ - { \ - typedef Default tname; \ - }; \ - \ - enum \ - { \ - value = (1 == sizeof(test(choose()))) \ - }; \ - \ - typedef typename boost::detail::if_true::BOOST_NESTED_TEMPLATE \ - then::type::tname type; \ - } - -#endif - -#define BOOST_UNORDERED_DEFAULT_TYPE(T, tname, arg) \ - typename default_type_##tname::type - namespace boost { namespace unordered { namespace detail { @@ -1023,6 +1029,7 @@ namespace boost { BOOST_UNORDERED_DEFAULT_TYPE_TMPLT( propagate_on_container_move_assignment); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(propagate_on_container_swap); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(is_always_equal); #if !defined(BOOST_NO_SFINAE_EXPR) @@ -1319,6 +1326,9 @@ namespace boost { false_type) propagate_on_container_move_assignment; typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, propagate_on_container_swap, false_type) propagate_on_container_swap; + + typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, is_always_equal, + typename boost::is_empty::type) is_always_equal; }; } } @@ -1339,9 +1349,20 @@ namespace boost { namespace unordered { namespace detail { + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(is_always_equal); + template struct allocator_traits : std::allocator_traits { + // As is_always_equal was introduced in C++17, std::allocator_traits + // doesn't always have it. So use it when available, implement it + // ourselves when not. Would be simpler not to bother with + // std::allocator_traits, but I feel like I should try to use + // it where possible. + typedef BOOST_UNORDERED_DEFAULT_TYPE(std::allocator_traits, + is_always_equal, + BOOST_UNORDERED_DEFAULT_TYPE(Alloc, is_always_equal, + typename boost::is_empty::type)) is_always_equal; }; template struct rebind_wrap diff --git a/test/unordered/allocator_traits.cpp b/test/unordered/allocator_traits.cpp index f59fc876..c6b06f85 100644 --- a/test/unordered/allocator_traits.cpp +++ b/test/unordered/allocator_traits.cpp @@ -126,6 +126,7 @@ void test_empty_allocator() BOOST_TEST(!traits::propagate_on_container_copy_assignment::value); BOOST_TEST(!traits::propagate_on_container_move_assignment::value); BOOST_TEST(!traits::propagate_on_container_swap::value); + BOOST_TEST(traits::is_always_equal::value); BOOST_TEST(call_select() == 0); } @@ -139,6 +140,7 @@ template struct allocator1 typedef yes_type propagate_on_container_copy_assignment; typedef yes_type propagate_on_container_move_assignment; typedef yes_type propagate_on_container_swap; + typedef yes_type is_always_equal; allocator1 select_on_container_copy_construction() const { @@ -166,6 +168,7 @@ void test_allocator1() BOOST_TEST(traits::propagate_on_container_copy_assignment::value); BOOST_TEST(traits::propagate_on_container_move_assignment::value); BOOST_TEST(traits::propagate_on_container_swap::value); + BOOST_TEST(traits::is_always_equal::value); BOOST_TEST(call_select() == 1); } @@ -192,6 +195,7 @@ template struct allocator2 : allocator2_base > typedef no_type propagate_on_container_copy_assignment; typedef no_type propagate_on_container_move_assignment; typedef no_type propagate_on_container_swap; + typedef no_type is_always_equal; }; void test_allocator2() @@ -208,6 +212,7 @@ void test_allocator2() BOOST_TEST(!traits::propagate_on_container_copy_assignment::value); BOOST_TEST(!traits::propagate_on_container_move_assignment::value); BOOST_TEST(!traits::propagate_on_container_swap::value); + BOOST_TEST(!traits::is_always_equal::value); BOOST_TEST(call_select() == 1); } @@ -240,6 +245,8 @@ template struct allocator3 typedef ptr const_pointer; typedef unsigned short size_type; + int x; // Just to make it non-empty, so that is_always_equal is false. + ALLOCATOR_METHODS_TYPEDEFS(allocator3) typedef yes_type propagate_on_container_copy_assignment; @@ -267,6 +274,7 @@ void test_allocator3() BOOST_TEST(traits::propagate_on_container_copy_assignment::value); BOOST_TEST(!traits::propagate_on_container_move_assignment::value); BOOST_TEST(!traits::propagate_on_container_swap::value); + BOOST_TEST(!traits::is_always_equal::value); BOOST_TEST(call_select() == 1); }