From 704951d3abb6cbcde678807af16de15f613afb8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Sun, 9 Aug 2026 20:26:27 +0200 Subject: [PATCH] Add support to accept allocator arguments with value_type == void. --- doc/container.qbk | 62 ++++++++++++++++ example/doc_void_allocator.cpp | 72 +++++++++++++++++++ include/boost/container/allocator_traits.hpp | 38 +++++++++- include/boost/container/deque.hpp | 5 +- include/boost/container/experimental/nest.hpp | 5 +- include/boost/container/hub.hpp | 6 +- include/boost/container/list.hpp | 5 +- include/boost/container/map.hpp | 6 +- include/boost/container/segtor.hpp | 5 +- include/boost/container/set.hpp | 6 +- include/boost/container/slist.hpp | 5 +- include/boost/container/small_vector.hpp | 2 +- include/boost/container/stable_vector.hpp | 4 +- include/boost/container/string.hpp | 4 +- include/boost/container/vector.hpp | 5 +- test/allocator_traits_test.cpp | 26 +++++++ test/deque_test.cpp | 11 +++ test/devector_test.cpp | 11 +++ test/flat_map_test.cpp | 11 +++ test/flat_set_test.cpp | 11 +++ test/hub_api_test.cpp | 12 +++- test/list_test.cpp | 11 +++ test/map_test.cpp | 12 ++++ test/nest_api_test.cpp | 10 +++ test/nest_test.cpp | 10 +++ test/pmr_deque_test.cpp | 13 ++++ test/pmr_devector_test.cpp | 13 ++++ test/pmr_flat_map_test.cpp | 13 ++++ test/pmr_flat_set_test.cpp | 13 ++++ test/pmr_hub_test.cpp | 13 ++++ test/pmr_list_test.cpp | 13 ++++ test/pmr_map_test.cpp | 13 ++++ test/pmr_nest_test.cpp | 13 ++++ test/pmr_set_test.cpp | 13 ++++ test/pmr_slist_test.cpp | 38 +++++++--- test/pmr_small_vector_test.cpp | 13 ++++ test/pmr_stable_vector_test.cpp | 13 ++++ test/pmr_string_test.cpp | 13 ++++ test/pmr_vector_test.cpp | 13 ++++ test/segtor_test.cpp | 11 +++ test/set_test.cpp | 12 ++++ test/slist_test.cpp | 11 +++ test/small_vector_test.cpp | 11 +++ test/stable_vector_test.cpp | 12 ++++ test/string_test.cpp | 12 +++- test/vector_test.cpp | 11 +++ test/void_allocator_test.hpp | 45 ++++++++++++ 47 files changed, 652 insertions(+), 35 deletions(-) create mode 100644 example/doc_void_allocator.cpp create mode 100644 test/void_allocator_test.hpp diff --git a/doc/container.qbk b/doc/container.qbk index 2a4d860..eb71a40 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -2050,6 +2050,65 @@ then the operation is constant time, even with an O(1) size. [endsect] +[section:void_allocator_argument The ['void] allocator argument] + +[*Boost.Container] treats `void` specially in the allocator template argument in two +related ways, so that the container's `allocator_type` always has the correct +`value_type`. This applies to sequence and associative containers alike +(including [classref boost::container::map map], where the rebound +`value_type` is `std::pair`). + +[section:void_allocator_type Allocator template argument is ['void]] + +When the allocator argument is the type `void`, the library selects its +default allocator for the container's `value_type`. + +[endsect] + +[section:void_value_type_allocator Allocator with ['void] value type] + +Standard containers typically require the allocator's `value_type` to match the +container's element type, which forces repeating that type in every declaration: + +`` +boost::container::vector > v; +boost::container::map > > m; +`` + +[*Boost.Container] also accepts an allocator whose `value_type` is `void`, +so that those types can be used as type-erased allocators. The container's +`allocator_type` is the rebound allocator. + +[import ../example/doc_void_allocator.cpp] +[doc_void_allocator] + +[*Benefits]. Accepting void-valued allocators has several advantages: + +* [*The element type is written once]. In `vector >` the + element type appears twice and both occurrences must be kept in sync. + +* [*It avoids a whole class of `value_type` mismatches in associative containers], + where the required allocator `value_type` is not the obvious one and is not even + the same across container families: + [classref boost::container::map map] requires + `MyAlloc< std::pair >` (note the `const`) whereas + [classref boost::container::flat_map flat_map] requires + `MyAlloc< std::pair >` (no `const`). Writing `MyAlloc` is correct + for both. + +* [*It removes rebinding boilerplate from generic code]. Code that is parameterized + on an allocator and instantiates containers of several element types no longer + needs to explicitly rebind, the allocator can be forwarded as a single template argument. + +Rebinding happens entirely at compile time, so there is no runtime cost. +Since the container's `allocator_type` is the rebound allocator, so +`AllocatorAwareContainer` semantics (propagation traits, `get_allocator()`, +allocator-extended constructors) are unchanged. + +[endsect] + +[endsect] + [endsect] [section:configurable_containers Extended functionality: Configurable containers] @@ -2834,6 +2893,9 @@ collect them containers and build [*Boost.Container], a library targeted to a wi [section:release_notes_boost_1_93_00 Boost 1.93 Release] +* Containers now accept allocators whose \c value_type is \c void; such allocators + are rebound to the container's element type via \c real_allocator. + * Added overaligned support for [classref boost::container::pmr::monotonic_buffer_resource monotonic_buffer_resource]. * Fixed bugs/issues/enhancements: diff --git a/example/doc_void_allocator.cpp b/example/doc_void_allocator.cpp new file mode 100644 index 0000000..cad4fbb --- /dev/null +++ b/example/doc_void_allocator.cpp @@ -0,0 +1,72 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2026. Distributed under 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) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#include + +//[doc_void_allocator +#include +#include +#include +#include +#include +//=#include + + +int main() +{ + using namespace boost::container; + +// +// void as Allocator template argument +// + //Allocator argument is void: the library selects its default allocator. + vector v_default; // Allocator defaults to void + vector v_explicit_void; // Explicit void + map m_default; // Allocator defaults to void + map, void> m_explicit_void; + + //Same types + v_default.push_back(1); + v_explicit_void = v_default; + + m_default[3] = 3.0; + m_explicit_void = m_default; + +// +// Automatic rebinding with allocator::value_type == void, +// + typedef pmr::polymorphic_allocator pmr_void_t; + typedef vector vector_alloc_of_void_t; + typedef map, pmr_void_t > map_alloc_of_void_t; + typedef std::pair map_value_t; + + /*<-*/ + BOOST_CONTAINER_STATIC_ASSERT + ((dtl::is_same< vector_alloc_of_void_t::allocator_type + , pmr::polymorphic_allocator >::value)); + BOOST_CONTAINER_STATIC_ASSERT + ((dtl::is_same< map_alloc_of_void_t::allocator_type + , pmr::polymorphic_allocator >::value)); + /*->*/ + //Container::allocator_type is the expected type + //=static_assert + //= (std::is_same< vector_alloc_of_void_t >::allocator_type, pmr::polymorphic_allocator >::value); + //=static_assert + //= (std::is_same< map_alloc_of_void_t >::allocator_type, pmr::polymorphic_allocator >::value); + + //Usually the Allocator type is convertible to the rebound allocator, no need + //to explicitly rebind it. + pmr_void_t alloc; + vector_alloc_of_void_t v(alloc); + map_alloc_of_void_t m(std::less(), alloc); + + return 0; +} +//] diff --git a/include/boost/container/allocator_traits.hpp b/include/boost/container/allocator_traits.hpp index a39e8ef..bc257cb 100644 --- a/include/boost/container/allocator_traits.hpp +++ b/include/boost/container/allocator_traits.hpp @@ -737,10 +737,46 @@ struct allocator_traits #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) +//! Obtains the allocator type used by a container from the allocator +//! template argument. If \c AllocatorOrVoid is \c void, yields +//! \c new_allocator. If \c AllocatorOrVoid::value_type is \c void, +//! yields the allocator rebound to \c T via \c portable_rebind_alloc. +//! Otherwise, yields \c AllocatorOrVoid unchanged. +//! +//! portable_rebind_alloc is only formed when value_type is void (partial +//! specialization). Eager if_c would break allocators that are not +//! pointer_rebind-able (e.g. static_storage_allocator). + +#if defined(BOOST_CONTAINER_GCC_COMPATIBLE_HAS_DIAGNOSTIC_IGNORED) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +template + < class T + , class AllocatorOrVoid + , bool = dtl::is_same::value + > +struct real_allocator_rebind +{ + typedef AllocatorOrVoid type; +}; + +template +struct real_allocator_rebind +{ + typedef typename allocator_traits::template + portable_rebind_alloc::type type; +}; + +#if defined(BOOST_CONTAINER_GCC_COMPATIBLE_HAS_DIAGNOSTIC_IGNORED) +#pragma GCC diagnostic pop +#endif + template struct real_allocator { - typedef AllocatorOrVoid type; + typedef typename real_allocator_rebind::type type; }; template diff --git a/include/boost/container/deque.hpp b/include/boost/container/deque.hpp index 22cbba6..d8626b7 100644 --- a/include/boost/container/deque.hpp +++ b/include/boost/container/deque.hpp @@ -40,8 +40,9 @@ namespace container { //! and removal of elements at the end of the sequence, and linear time insertion and removal of elements in the middle. //! //! \tparam T The type of object that is stored in the deque -//! \tparam A The allocator used for all internal memory management, use void -//! for the default allocator +//! \tparam A The allocator used for all internal memory management. Use void +//! for the default allocator. An allocator whose \c value_type is \c void +//! is rebound to \c T. //! \tparam Options A type produced from \c boost::container::deque_options. template #else diff --git a/include/boost/container/experimental/nest.hpp b/include/boost/container/experimental/nest.hpp index 0ba5322..6bd0924 100644 --- a/include/boost/container/experimental/nest.hpp +++ b/include/boost/container/experimental/nest.hpp @@ -1065,8 +1065,9 @@ struct get_nest_opt > //! efficient insertion and erasure operations in constant time. //! //! \tparam T The type of object stored in the nest -//! \tparam Allocator The allocator used for all internal memory management, use void -//! for the default allocator +//! \tparam Allocator The allocator used for all internal memory management. Use void +//! for the default allocator. An allocator whose \c value_type is \c void +//! is rebound to \c T. //! \tparam Options A type produced from \c boost::container::nest_options (e.g. \c store_data_in_block). #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED template diff --git a/include/boost/container/hub.hpp b/include/boost/container/hub.hpp index fade29a..93e59d7 100644 --- a/include/boost/container/hub.hpp +++ b/include/boost/container/hub.hpp @@ -921,7 +921,8 @@ struct block_typedefs //! //! \tparam T The cv-unqualified object type of the elements stored in the hub. //! \tparam Allocator An allocator whose value type is \c T. If \c void (the -//! default), \c boost::container::new_allocator is used. +//! default), \c boost::container::new_allocator is used. An allocator +//! whose \c value_type is \c void is rebound to \c T. //! //! Exception safety: Except when explicitly noted, all non-const member //! functions (and free functions taking \c hub by non-const reference) provide @@ -937,7 +938,8 @@ class hub #endif { public: - //! new_allocator is if Allocator is void, an alias for Allocator otherwise. + //! \c new_allocator if Allocator is void, the allocator rebound to \c T + //! if Allocator's \c value_type is \c void, otherwise an alias for Allocator. typedef BOOST_CONTAINER_IMPDEF (typename real_allocator::type) allocator_type; diff --git a/include/boost/container/list.hpp b/include/boost/container/list.hpp index 6103305..0a6f382 100644 --- a/include/boost/container/list.hpp +++ b/include/boost/container/list.hpp @@ -108,8 +108,9 @@ struct intrusive_list_type //! or mutation is explicit. //! //! \tparam T The type of object that is stored in the list -//! \tparam Allocator The allocator used for all internal memory management, use void -//! for the default allocator +//! \tparam Allocator The allocator used for all internal memory management. Use void +//! for the default allocator. An allocator whose \c value_type is \c void +//! is rebound to \c T. #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED template class list diff --git a/include/boost/container/map.hpp b/include/boost/container/map.hpp index 7a7853f..96e46f8 100644 --- a/include/boost/container/map.hpp +++ b/include/boost/container/map.hpp @@ -67,7 +67,8 @@ namespace container { //! \tparam T is the mapped_type //! \tparam Compare is the ordering function for Keys (e.g. std::less). //! \tparam Allocator is the allocator to allocate the value_types -//! (e.g. allocator< std::pair > ). +//! (e.g. allocator< std::pair > ). An allocator whose +//! \c value_type is \c void is rebound to the map's \c value_type. //! \tparam Options is an packed option type generated using using boost::container::tree_assoc_options. template < class Key, class T, class Compare = std::less , class Allocator = void, class Options = tree_assoc_defaults > @@ -1610,7 +1611,8 @@ namespace container { //! \tparam Value is the mapped_type //! \tparam Compare is the ordering function for Keys (e.g. std::less). //! \tparam Allocator is the allocator to allocate the value_types -//! (e.g. allocator< std::pair > ). +//! (e.g. allocator< std::pair > ). An allocator whose +//! \c value_type is \c void is rebound to the map's \c value_type. //! \tparam Options is an packed option type generated using using boost::container::tree_assoc_options. template < class Key, class T, class Compare = std::less , class Allocator = new_allocator< std::pair< const Key, T> >, class Options = tree_assoc_defaults> diff --git a/include/boost/container/segtor.hpp b/include/boost/container/segtor.hpp index 748e380..e86a761 100644 --- a/include/boost/container/segtor.hpp +++ b/include/boost/container/segtor.hpp @@ -36,8 +36,9 @@ namespace container { //! pop_front, or emplace_front. //! //! \tparam T The type of object that is stored in the segtor -//! \tparam Allocator The allocator used for all internal memory management, use void -//! for the default allocator +//! \tparam Allocator The allocator used for all internal memory management. Use void +//! for the default allocator. An allocator whose \c value_type is \c void +//! is rebound to \c T. //! \tparam Options A type produced from \c boost::container::segtor_options. template #else diff --git a/include/boost/container/set.hpp b/include/boost/container/set.hpp index 9a76a02..e59cd41 100644 --- a/include/boost/container/set.hpp +++ b/include/boost/container/set.hpp @@ -59,7 +59,8 @@ namespace container { //! //! \tparam Key is the type to be inserted in the set, which is also the key_type //! \tparam Compare is the comparison functor used to order keys -//! \tparam Allocator is the allocator to be used to allocate memory for this container +//! \tparam Allocator is the allocator to be used to allocate memory for this container. +//! An allocator whose \c value_type is \c void is rebound to \c Key. //! \tparam Options is an packed option type generated using using boost::container::tree_assoc_options. template , class Allocator = new_allocator, class Options = void> #else @@ -1139,7 +1140,8 @@ namespace container { //! //! \tparam Key is the type to be inserted in the set, which is also the key_type //! \tparam Compare is the comparison functor used to order keys -//! \tparam Allocator is the allocator to be used to allocate memory for this container +//! \tparam Allocator is the allocator to be used to allocate memory for this container. +//! An allocator whose \c value_type is \c void is rebound to \c Key. //! \tparam Options is an packed option type generated using using boost::container::tree_assoc_options. template , class Allocator = new_allocator, class Options = tree_assoc_defaults > #else diff --git a/include/boost/container/slist.hpp b/include/boost/container/slist.hpp index badaac0..fecae2c 100644 --- a/include/boost/container/slist.hpp +++ b/include/boost/container/slist.hpp @@ -135,8 +135,9 @@ struct intrusive_slist_type //! then you should probably use list instead of slist. //! //! \tparam T The type of object that is stored in the list -//! \tparam Allocator The allocator used for all internal memory management, use void -//! for the default allocator +//! \tparam Allocator The allocator used for all internal memory management. Use void +//! for the default allocator. An allocator whose \c value_type is \c void +//! is rebound to \c T. #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED template class slist diff --git a/include/boost/container/small_vector.hpp b/include/boost/container/small_vector.hpp index f070e52..0ffb28e 100644 --- a/include/boost/container/small_vector.hpp +++ b/include/boost/container/small_vector.hpp @@ -802,7 +802,7 @@ inline typename small_vector_allocator::pointer //! \tparam T The type of object that is stored in the small_vector //! \tparam N The number of preallocated elements stored inside small_vector. It shall be less than Allocator::max_size(); //! \tparam Allocator The allocator used for memory management when the number of elements exceeds N. Use void -//! for the default allocator +//! for the default allocator. An allocator whose \c value_type is \c void is rebound to \c T. //! \tparam Options A type produced from \c boost::container::small_vector_options. template class small_vector diff --git a/include/boost/container/stable_vector.hpp b/include/boost/container/stable_vector.hpp index fdff95c..66a35e6 100644 --- a/include/boost/container/stable_vector.hpp +++ b/include/boost/container/stable_vector.hpp @@ -497,7 +497,9 @@ class stable_vector_iterator //! operations provide stronger exception safety guarantees than in std::vector. //! //! \tparam T The type of object that is stored in the stable_vector -//! \tparam Allocator The allocator used for all internal memory management +//! \tparam Allocator The allocator used for all internal memory management. +//! Use void for the default allocator. An allocator whose \c value_type is +//! \c void is rebound to \c T. #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED template #else diff --git a/include/boost/container/string.hpp b/include/boost/container/string.hpp index ab2210f..7f2cc2e 100644 --- a/include/boost/container/string.hpp +++ b/include/boost/container/string.hpp @@ -602,7 +602,9 @@ class basic_string_base //! //! \tparam CharT The type of character it contains. //! \tparam Traits The Character Traits type, which encapsulates basic character operations -//! \tparam Allocator The allocator, used for internal memory management. +//! \tparam Allocator The allocator, used for internal memory management. Use void +//! for the default allocator. An allocator whose \c value_type is \c void +//! is rebound to \c CharT. //! \tparam Options A type produced from \c boost::container::string_options. #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED template , class Allocator = void, class Options = void > diff --git a/include/boost/container/vector.hpp b/include/boost/container/vector.hpp index 603c847..5e3da98 100644 --- a/include/boost/container/vector.hpp +++ b/include/boost/container/vector.hpp @@ -821,8 +821,9 @@ struct get_vector_opt //! elements in a vector may vary dynamically; memory management is automatic. //! //! \tparam T The type of object that is stored in the vector -//! \tparam A The allocator used for all internal memory management, use void -//! for the default allocator +//! \tparam A The allocator used for all internal memory management. Use void +//! for the default allocator. An allocator whose \c value_type is \c void +//! is rebound to \c T. //! \tparam Options A type produced from \c boost::container::vector_options. template class vector diff --git a/test/allocator_traits_test.cpp b/test/allocator_traits_test.cpp index 255e23f..3bbb80d 100644 --- a/test/allocator_traits_test.cpp +++ b/test/allocator_traits_test.cpp @@ -9,6 +9,8 @@ ////////////////////////////////////////////////////////////////////////////// #include #include +#include +#include #include #include #include @@ -17,6 +19,7 @@ #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) #include #endif +#include "dummy_test_allocator.hpp" #include "lightweight_test.hpp" template @@ -254,10 +257,33 @@ void test_void_allocator() boost::container::allocator_traits > comtraits; (void)comtraits; } +void test_real_allocator_void_value_type() +{ + using namespace boost::container; + using namespace boost::container::dtl; + + BOOST_CONTAINER_STATIC_ASSERT((is_same::type, new_allocator >::value)); + BOOST_CONTAINER_STATIC_ASSERT((is_same >::type, SimpleAllocator >::value)); + BOOST_CONTAINER_STATIC_ASSERT((is_same >::type, SimpleAllocator >::value)); + BOOST_CONTAINER_STATIC_ASSERT((is_same >::type, ComplexAllocator >::value)); + + typedef vector > vector_t; + BOOST_CONTAINER_STATIC_ASSERT((is_same >::value)); + + test::simple_allocator a; + vector_t v(a); + v.push_back(1); + v.push_back(2); + BOOST_TEST(v.size() == 2u); + BOOST_TEST(v[0] == 1); + BOOST_TEST(v[1] == 2); +} + int main() { using namespace boost::container::dtl; test_void_allocator(); + test_real_allocator_void_value_type(); //SimpleAllocator BOOST_CONTAINER_STATIC_ASSERT(( is_same #include "emplace_test.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "vector_test.hpp" #include "default_init_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -461,6 +462,16 @@ int main () } + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < deque > + , new_allocator >()) { + std::cerr << "test_void_allocator deque failed" << std::endl; + return 1; + } + //////////////////////////////////// // Allocator implementations //////////////////////////////////// diff --git a/test/devector_test.cpp b/test/devector_test.cpp index 7425764..b796f2c 100644 --- a/test/devector_test.cpp +++ b/test/devector_test.cpp @@ -25,6 +25,7 @@ #include #include "dummy_test_allocator.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "check_equal_containers.hpp" #include "movable_int.hpp" @@ -3905,6 +3906,16 @@ int main() // Allocator implementations //////////////////////////////////// // std:allocator + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < devector > + , new_allocator >()) { + std::cerr << "test_void_allocator devector failed" << std::endl; + return 1; + } + if (test_cont_variants< std::allocator >()) { std::cerr << "test_cont_variants< std::allocator > failed" << std::endl; return 1; diff --git a/test/flat_map_test.cpp b/test/flat_map_test.cpp index fa63626..bcace58 100644 --- a/test/flat_map_test.cpp +++ b/test/flat_map_test.cpp @@ -18,6 +18,7 @@ #include "movable_int.hpp" #include "flat_map_test.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "container_common_tests.hpp" #include "emplace_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -364,6 +365,16 @@ int main() if (!test_heterogeneous_lookup_by_partial_key()) return 1; + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < flat_map, new_allocator > + , new_allocator< std::pair > >()) { + std::cerr << "test_void_allocator flat_map failed" << std::endl; + return 1; + } + //////////////////////////////////// // Testing allocator implementations //////////////////////////////////// diff --git a/test/flat_set_test.cpp b/test/flat_set_test.cpp index ee37b25..b91e4e9 100644 --- a/test/flat_set_test.cpp +++ b/test/flat_set_test.cpp @@ -24,6 +24,7 @@ #include "movable_int.hpp" #include "set_test.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "emplace_test.hpp" #include "container_common_tests.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -685,6 +686,16 @@ int main() return 1; } + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < flat_set, new_allocator > + , new_allocator >()) { + std::cerr << "test_void_allocator flat_set failed" << std::endl; + return 1; + } + //////////////////////////////////// // Testing allocator implementations //////////////////////////////////// diff --git a/test/hub_api_test.cpp b/test/hub_api_test.cpp index 6dbe632..7c3c8f4 100644 --- a/test/hub_api_test.cpp +++ b/test/hub_api_test.cpp @@ -17,6 +17,8 @@ int main() { return 0; } #include #include #include +#include "void_allocator_test.hpp" +#include #include #include #include @@ -783,7 +785,15 @@ int main() test_ctad(); - return boost::report_errors(); + + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + BOOST_TEST((boost::container::test::test_void_allocator + < boost::container::hub > + , boost::container::new_allocator >())); + + return boost::report_errors(); } #endif diff --git a/test/list_test.cpp b/test/list_test.cpp index e8077d9..14e2992 100644 --- a/test/list_test.cpp +++ b/test/list_test.cpp @@ -17,6 +17,7 @@ #include "movable_int.hpp" #include "list_test.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "emplace_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -175,6 +176,16 @@ int main () move_assign.swap(original); } + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < list > + , new_allocator >()) { + std::cerr << "test_void_allocator list failed" << std::endl; + return 1; + } + //////////////////////////////////// // Testing allocator implementations //////////////////////////////////// diff --git a/test/map_test.cpp b/test/map_test.cpp index dd27164..92c5993 100644 --- a/test/map_test.cpp +++ b/test/map_test.cpp @@ -11,12 +11,14 @@ #include #include +#include #include "print_container.hpp" #include "movable_int.hpp" #include "dummy_test_allocator.hpp" #include "map_test.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "emplace_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -418,6 +420,16 @@ int main () s.emplace(p); } + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < map, new_allocator > + , new_allocator< std::pair > >()) { + std::cerr << "test_void_allocator map failed" << std::endl; + return 1; + } + //////////////////////////////////// // Testing allocator implementations //////////////////////////////////// diff --git a/test/nest_api_test.cpp b/test/nest_api_test.cpp index 702754f..0db2cec 100644 --- a/test/nest_api_test.cpp +++ b/test/nest_api_test.cpp @@ -14,6 +14,8 @@ ////////////////////////////////////////////////////////////////////////////// #include +#include "void_allocator_test.hpp" +#include #include "lightweight_test.hpp" #include #include @@ -995,5 +997,13 @@ int main() test_move_with_unequal_allocators(); } + + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + BOOST_TEST((test::test_void_allocator + < nest > + , new_allocator >())); + return boost::report_errors(); } diff --git a/test/nest_test.cpp b/test/nest_test.cpp index 170c5a0..d17abea 100644 --- a/test/nest_test.cpp +++ b/test/nest_test.cpp @@ -9,6 +9,8 @@ ////////////////////////////////////////////////////////////////////////////// #include +#include "void_allocator_test.hpp" +#include #include "lightweight_test.hpp" #include #include @@ -801,5 +803,13 @@ int main() #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) test_initializer_list_operations(); #endif + + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + BOOST_TEST((test::test_void_allocator + < nest > + , new_allocator >())); + return boost::report_errors(); } diff --git a/test/pmr_deque_test.cpp b/test/pmr_deque_test.cpp index 81526bc..ddd7a68 100644 --- a/test/pmr_deque_test.cpp +++ b/test/pmr_deque_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -24,5 +25,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.push_back(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef deque > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_devector_test.cpp b/test/pmr_devector_test.cpp index ee90fb3..1688e68 100644 --- a/test/pmr_devector_test.cpp +++ b/test/pmr_devector_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -25,5 +26,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.push_back(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef devector > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_flat_map_test.cpp b/test/pmr_flat_map_test.cpp index 77140c0..4a5a00a 100644 --- a/test/pmr_flat_map_test.cpp +++ b/test/pmr_flat_map_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -24,5 +25,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.insert(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef flat_map, pmr::polymorphic_allocator > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator< std::pair > >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_flat_set_test.cpp b/test/pmr_flat_set_test.cpp index b1ce438..1c357b0 100644 --- a/test/pmr_flat_set_test.cpp +++ b/test/pmr_flat_set_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -24,5 +25,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.insert(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef flat_set, pmr::polymorphic_allocator > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_hub_test.cpp b/test/pmr_hub_test.cpp index 47206e3..08a2e90 100644 --- a/test/pmr_hub_test.cpp +++ b/test/pmr_hub_test.cpp @@ -16,6 +16,7 @@ int main() { return 0; } #include #include +#include "void_allocator_test.hpp" int main() { @@ -31,6 +32,18 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.insert(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef hub > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_list_test.cpp b/test/pmr_list_test.cpp index 10b7672..36707a3 100644 --- a/test/pmr_list_test.cpp +++ b/test/pmr_list_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -24,5 +25,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.push_back(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef list > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_map_test.cpp b/test/pmr_map_test.cpp index 64ac63a..49b8645 100644 --- a/test/pmr_map_test.cpp +++ b/test/pmr_map_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -24,5 +25,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.insert(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef map, pmr::polymorphic_allocator > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator< std::pair > >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_nest_test.cpp b/test/pmr_nest_test.cpp index b872ef4..9aad20c 100644 --- a/test/pmr_nest_test.cpp +++ b/test/pmr_nest_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -24,5 +25,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.insert(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef nest > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_set_test.cpp b/test/pmr_set_test.cpp index 014818b..008b71b 100644 --- a/test/pmr_set_test.cpp +++ b/test/pmr_set_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" #include int main() @@ -26,5 +27,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.insert(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef set, pmr::polymorphic_allocator > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_slist_test.cpp b/test/pmr_slist_test.cpp index ed4b5ae..0f2725d 100644 --- a/test/pmr_slist_test.cpp +++ b/test/pmr_slist_test.cpp @@ -8,19 +8,35 @@ // ////////////////////////////////////////////////////////////////////////////// -#include - -struct empty -{ - friend bool operator == (const empty &, const empty &){ return true; } - friend bool operator < (const empty &, const empty &){ return true; } -}; - -template class ::boost::container::slist; +#include +#include +#include "void_allocator_test.hpp" int main() { - ::boost::container::slist dummy; - (void)dummy; + using namespace boost::container; + using boost::container::dtl::is_same; + + typedef slist > intcontainer_t; + BOOST_CONTAINER_STATIC_ASSERT(( is_same::type >::value )); + #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) + BOOST_CONTAINER_STATIC_ASSERT(( is_same >::value )); + #endif + intcontainer_t cont(pmr::get_default_resource()); + typedef intcontainer_t::value_type value_type; + cont.push_front(value_type()); + + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef slist > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_small_vector_test.cpp b/test/pmr_small_vector_test.cpp index 9126df1..12a6eeb 100644 --- a/test/pmr_small_vector_test.cpp +++ b/test/pmr_small_vector_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -26,5 +27,17 @@ int main() typedef intcontainer_t::value_type value_type; cont.push_back(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef small_vector > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_stable_vector_test.cpp b/test/pmr_stable_vector_test.cpp index c9f021a..7e578df 100644 --- a/test/pmr_stable_vector_test.cpp +++ b/test/pmr_stable_vector_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -24,5 +25,17 @@ int main() intcontainer_t cont(pmr::get_default_resource()); typedef intcontainer_t::value_type value_type; cont.push_back(value_type()); + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef stable_vector > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_string_test.cpp b/test/pmr_string_test.cpp index 9b62c0d..47effcb 100644 --- a/test/pmr_string_test.cpp +++ b/test/pmr_string_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -26,5 +27,17 @@ int main() BOOST_CONTAINER_STATIC_ASSERT(( is_same::value )); BOOST_CONTAINER_STATIC_ASSERT(( is_same::value )); #endif + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef basic_string, pmr::polymorphic_allocator > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/pmr_vector_test.cpp b/test/pmr_vector_test.cpp index 6352b6d..615fb45 100644 --- a/test/pmr_vector_test.cpp +++ b/test/pmr_vector_test.cpp @@ -10,6 +10,7 @@ #include #include +#include "void_allocator_test.hpp" int main() { @@ -21,5 +22,17 @@ int main() #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) BOOST_CONTAINER_STATIC_ASSERT(( is_same >::value )); #endif + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + { + typedef vector > voidalloc_cont_t; + if(!test::test_void_allocator + < voidalloc_cont_t + , pmr::polymorphic_allocator >()) { + return 1; + } + } + return 0; } diff --git a/test/segtor_test.cpp b/test/segtor_test.cpp index ee50479..8545997 100644 --- a/test/segtor_test.cpp +++ b/test/segtor_test.cpp @@ -30,6 +30,7 @@ #include #include "emplace_test.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "vector_test.hpp" #include "default_init_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -448,6 +449,16 @@ int main () d.resize(1); } + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < segtor > + , new_allocator >()) { + std::cerr << "test_void_allocator segtor failed" << std::endl; + return 1; + } + //////////////////////////////////// // Allocator implementations //////////////////////////////////// diff --git a/test/set_test.cpp b/test/set_test.cpp index 93f4d70..fa8227e 100644 --- a/test/set_test.cpp +++ b/test/set_test.cpp @@ -9,6 +9,7 @@ // ////////////////////////////////////////////////////////////////////////////// #include +#include #include #include @@ -17,6 +18,7 @@ #include "dummy_test_allocator.hpp" #include "set_test.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "emplace_test.hpp" #include "../../intrusive/test/iterator_test.hpp" #include //for std::pair @@ -434,6 +436,16 @@ int main () >()) return 1; + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < set, new_allocator > + , new_allocator >()) { + std::cerr << "test_void_allocator set failed" << std::endl; + return 1; + } + //////////////////////////////////// // Testing allocator implementations //////////////////////////////////// diff --git a/test/slist_test.cpp b/test/slist_test.cpp index 107d25b..3728fb2 100644 --- a/test/slist_test.cpp +++ b/test/slist_test.cpp @@ -16,6 +16,7 @@ #include "movable_int.hpp" #include "list_test.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "emplace_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -194,6 +195,16 @@ int main () } } } + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < slist > + , new_allocator >()) { + std::cerr << "test_void_allocator slist failed" << std::endl; + return 1; + } + //////////////////////////////////// // Testing allocator implementations //////////////////////////////////// diff --git a/test/small_vector_test.cpp b/test/small_vector_test.cpp index 01b81bf..fcca145 100644 --- a/test/small_vector_test.cpp +++ b/test/small_vector_test.cpp @@ -12,6 +12,7 @@ #include "vector_test.hpp" #include "movable_int.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "default_init_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -487,6 +488,16 @@ int main() if (test::vector_test< small_vector >()) return 1; + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < small_vector > + , new_allocator >()) { + std::cerr << "test_void_allocator small_vector failed" << std::endl; + return 1; + } + if (test_cont_variants< new_allocator >()) return 1; diff --git a/test/stable_vector_test.cpp b/test/stable_vector_test.cpp index b620b9f..a7bef34 100644 --- a/test/stable_vector_test.cpp +++ b/test/stable_vector_test.cpp @@ -19,6 +19,8 @@ #include "expand_bwd_test_template.hpp" #include "dummy_test_allocator.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" +#include #include "vector_test.hpp" #include "default_init_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -127,6 +129,16 @@ int main() sv.resize(1); } + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < stable_vector > + , new_allocator >()) { + std::cerr << "test_void_allocator stable_vector failed" << std::endl; + return 1; + } + //////////////////////////////////// // Testing allocator implementations //////////////////////////////////// diff --git a/test/string_test.cpp b/test/string_test.cpp index 5e9b8df..c89a757 100644 --- a/test/string_test.cpp +++ b/test/string_test.cpp @@ -14,6 +14,8 @@ #include #include +#include "void_allocator_test.hpp" +#include #include #include #include //equal() @@ -2489,5 +2491,13 @@ int main() test_with_lightweight_test(); - return boost::report_errors(); + + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + BOOST_TEST((test::test_void_allocator + < basic_string, new_allocator > + , new_allocator >())); + +return boost::report_errors(); } diff --git a/test/vector_test.cpp b/test/vector_test.cpp index 681e18a..adb9f66 100644 --- a/test/vector_test.cpp +++ b/test/vector_test.cpp @@ -28,6 +28,7 @@ #include "expand_bwd_test_allocator.hpp" #include "expand_bwd_test_template.hpp" #include "propagate_allocator_test.hpp" +#include "void_allocator_test.hpp" #include "vector_test.hpp" #include "default_init_test.hpp" #include "../../intrusive/test/iterator_test.hpp" @@ -271,6 +272,16 @@ int main() move_assign.swap(original); } + //////////////////////////////////// + // Void value_type allocator + //////////////////////////////////// + if(!test::test_void_allocator + < vector > + , new_allocator >()) { + std::cerr << "test_void_allocator vector failed" << std::endl; + return 1; + } + //////////////////////////////////// // Testing allocator implementations //////////////////////////////////// diff --git a/test/void_allocator_test.hpp b/test/void_allocator_test.hpp new file mode 100644 index 0000000..c201f16 --- /dev/null +++ b/test/void_allocator_test.hpp @@ -0,0 +1,45 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2026. Distributed under 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) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_CONTAINER_TEST_VOID_ALLOCATOR_TEST_HEADER +#define BOOST_CONTAINER_TEST_VOID_ALLOCATOR_TEST_HEADER + +#include +#include +#include + +namespace boost { +namespace container { +namespace test { + +//! Checks that \c Container (instantiated with an allocator whose +//! \c value_type is \c void, or with \c void itself) exposes the expected +//! rebound \c allocator_type and can insert one default-constructed value. +template +inline bool test_void_allocator() +{ + typedef typename Container::allocator_type allocator_type; + typedef typename Container::value_type value_type; + + BOOST_CONTAINER_STATIC_ASSERT((dtl::is_same::value)); + BOOST_CONTAINER_STATIC_ASSERT((dtl::is_same + < typename allocator_traits::value_type + , value_type >::value)); + + Container c; + c.insert(c.cend(), value_type()); + return c.size() == typename Container::size_type(1); +} + +} //namespace test { +} //namespace container { +} //namespace boost { + +#endif //BOOST_CONTAINER_TEST_VOID_ALLOCATOR_TEST_HEADER