Fixes #99 ("some intrusive containers are not trivially destructible when possible")

Enables conditional triviality with C++20 concepts, introducing defaulted destructors and constructors for hooks and
containers using `normal_link`.
This commit is contained in:
Ion Gaztañaga
2025-12-08 21:31:48 +01:00
parent 2d25ad5397
commit 410f7a631b
8 changed files with 243 additions and 4 deletions

View File

@@ -3905,6 +3905,19 @@ to be inserted in intrusive containers are allocated using `std::vector` or `std
[section:release_notes Release Notes]
[section:release_notes_boost_1_91_00 Boost 1.91 Release]
* Hooks and containers using `normal_link`s now have defaulted destructors if C++20 concepts are available. This allows
trivially destructible hooks and containers if internally used nodes and comparison objects are trivially destructible.
* Hooks using `normal_link`s now have defaulted constructors if C++20 concepts are available. This allows
trivially default constructible hooks if internally used nodes are trivially destructible.
* Fixed bugs:
* [@https://github.com/boostorg/intrusive/issues/99 GitHub #92: ['some intrusive containers are not trivially destructible when possible]]
[endsect]
[section:release_notes_boost_1_89_00 Boost 1.89 Release]
* Fixed bugs:

View File

@@ -585,6 +585,9 @@ struct bstbase
//Detach all inserted nodes. This will add exception safety to bstree_impl
//constructors inserting elements.
~bstbase()
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
requires (ValueTraits::link_mode != normal_link)
#endif
{
if(is_safe_autounlink<value_traits::link_mode>::value){
node_algorithms::clear_and_dispose
@@ -594,6 +597,11 @@ struct bstbase
node_algorithms::init(this->header_ptr());
}
}
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
//Default destructor for normal links (allows conditional triviality)
~bstbase() requires (ValueTraits::link_mode == normal_link) = default;
#endif
};

View File

@@ -170,12 +170,20 @@ class generic_hook
/// @endcond
inline generic_hook() BOOST_NOEXCEPT
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
requires (LinkMode != normal_link)
#endif
{
if(hooktags::safemode_or_autounlink){
node_algorithms::init(this->this_ptr());
}
}
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
//Default destructor for normal links (allows conditional triviality)
generic_hook() requires (LinkMode == normal_link) = default;
#endif
inline generic_hook(const generic_hook& ) BOOST_NOEXCEPT
{
if(hooktags::safemode_or_autounlink){
@@ -187,11 +195,19 @@ class generic_hook
{ return *this; }
inline ~generic_hook()
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
requires (LinkMode != normal_link)
#endif
{
destructor_impl
(*this, detail::link_dispatch<hooktags::link_mode>());
}
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
//Default destructor for normal links (allows conditional triviality)
~generic_hook() requires (LinkMode == normal_link) = default;
#endif
inline void swap_nodes(generic_hook &other) BOOST_NOEXCEPT
{
node_algorithms::swap_nodes

View File

@@ -121,5 +121,8 @@ template<unsigned> struct static_assert_test {};
# define BOOST_INTRUSIVE_NO_DANGLING
#endif
#if defined(__cpp_concepts) && (__cpp_concepts >= 202002L)
# define BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING
#endif
#endif //#ifndef BOOST_INTRUSIVE_DETAIL_WORKAROUND_HPP

View File

@@ -1909,7 +1909,19 @@ struct hashdata_internal
{ return this->priv_size_traits(); }
~hashdata_internal()
{ this->priv_clear_buckets(); }
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
requires (ValueTraits::link_mode != normal_link)
#endif
{
BOOST_IF_CONSTEXPR(safemode_or_autounlink){
this->priv_clear_buckets();
}
}
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
//Default destructor for normal links (allows conditional triviality)
~hashdata_internal() requires (ValueTraits::link_mode == normal_link) = default;
#endif
using split_bucket_hash_equal_t::priv_clear_buckets;
@@ -2462,6 +2474,7 @@ class hashtable_impl
hashtable_impl& operator=(BOOST_RV_REF(hashtable_impl) x)
{ this->swap(x); return *this; }
#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
//! <b>Effects</b>: Detaches all elements from this. The objects in the unordered_set
//! are not deleted (i.e. no destructors are called).
//!
@@ -2469,10 +2482,8 @@ class hashtable_impl
//! it's a safe-mode or auto-unlink value. Otherwise constant.
//!
//! <b>Throws</b>: Nothing.
~hashtable_impl()
{}
~hashtable_impl();
#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
//! <b>Effects</b>: Returns an iterator pointing to the beginning of the unordered_set.
//!
//! <b>Complexity</b>: Amortized constant time.

View File

@@ -249,6 +249,9 @@ class list_impl
//! <b>Complexity</b>: Linear to the number of elements in the list, if
//! it's a safe-mode or auto-unlink value . Otherwise constant.
~list_impl()
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
requires (ValueTraits::link_mode != normal_link)
#endif
{
BOOST_IF_CONSTEXPR(is_safe_autounlink<ValueTraits::link_mode>::value){
this->clear();
@@ -256,6 +259,11 @@ class list_impl
}
}
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
//Default destructor for normal links (allows conditional triviality)
~list_impl() requires (ValueTraits::link_mode == normal_link) = default;
#endif
//! <b>Requires</b>: value must be an lvalue.
//!
//! <b>Effects</b>: Inserts the value in the back of the list.

View File

@@ -367,6 +367,9 @@ class slist_impl
//! <b>Complexity</b>: Linear to the number of elements in the list, if
//! it's a safe-mode or auto-unlink value. Otherwise constant.
~slist_impl()
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
requires (ValueTraits::link_mode != normal_link)
#endif
{
BOOST_IF_CONSTEXPR(is_safe_autounlink<ValueTraits::link_mode>::value){
this->clear();
@@ -374,6 +377,11 @@ class slist_impl
}
}
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
//Default destructor for normal links (allows conditional triviality)
~slist_impl() requires (ValueTraits::link_mode == normal_link) = default;
#endif
//! <b>Effects</b>: Erases all the elements of the container.
//!
//! <b>Throws</b>: Nothing.

View File

@@ -0,0 +1,172 @@
/////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2025-2025
//
// 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/intrusive for documentation.
//
/////////////////////////////////////////////////////////////////////////////
#include <boost/config.hpp>
//Conditional triviality is based on destructor overloads based on concepts
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
#include <type_traits>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/slist.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/intrusive/unordered_set.hpp>
#include <boost/intrusive/splay_set.hpp>
#include <boost/intrusive/avl_set.hpp>
#include <boost/intrusive/sg_set.hpp>
#include <boost/intrusive/treap_set.hpp>
#include <boost/intrusive/bs_set.hpp>
#include <boost/intrusive/pointer_traits.hpp>
#include <boost/intrusive/any_hook.hpp>
using namespace boost::intrusive;
typedef list_base_hook
< void_pointer<void*>, link_mode<normal_link> > list_base_hook_t;
typedef slist_base_hook
< void_pointer<void*>, link_mode<normal_link> > slist_base_hook_t;
typedef set_base_hook
< void_pointer<void*>, link_mode<normal_link> > set_base_hook_t;
typedef avl_set_base_hook
< void_pointer<void*>, link_mode<normal_link> > avl_base_hook_t;
typedef bs_set_base_hook
< void_pointer<void*>, link_mode<normal_link> > bs_base_hook_t;
typedef unordered_set_base_hook
< void_pointer<void*>, link_mode<normal_link> > unordered_base_hook_t;
typedef any_base_hook
< void_pointer<void*>, link_mode<normal_link> > any_base_hook_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<list_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<slist_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<set_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<avl_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<bs_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<unordered_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<any_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<list_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<slist_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<set_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<avl_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<bs_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<unordered_base_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<any_base_hook_t> ));
typedef list_member_hook
< void_pointer<void*>, link_mode<normal_link> > list_member_hook_t;
typedef slist_member_hook
< void_pointer<void*>, link_mode<normal_link> > slist_member_hook_t;
typedef set_member_hook
< void_pointer<void*>, link_mode<normal_link> > set_member_hook_t;
typedef avl_set_member_hook
< void_pointer<void*>, link_mode<normal_link> > avl_member_hook_t;
typedef bs_set_member_hook
< void_pointer<void*>, link_mode<normal_link> > bs_member_hook_t;
typedef unordered_set_member_hook
< void_pointer<void*>, link_mode<normal_link> > unordered_member_hook_t;
typedef any_member_hook
< void_pointer<void*>, link_mode<normal_link> > any_member_hook_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<list_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<slist_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<set_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<avl_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<bs_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<unordered_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<any_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<list_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<slist_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<set_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<avl_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<bs_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<unordered_member_hook_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<any_member_hook_t> ));
struct MyType
: public list_base_hook_t
, public slist_base_hook_t
, public set_base_hook_t
, public avl_base_hook_t
, public bs_base_hook_t
, public unordered_base_hook_t
, public any_base_hook_t
{
list_member_hook_t limh;
slist_member_hook_t slmh;
set_member_hook_t semh;
avl_member_hook_t avmh;
bs_member_hook_t bsmh;
unordered_member_hook_t unmh;
any_member_hook_t anmh;
friend bool operator<(const MyType &, const MyType &) { return true; }
friend std::size_t hash_value(const MyType &, const MyType &) { return 0u; }
};
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<MyType> ));
typedef list<MyType> list_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<list_t> ));
typedef slist<MyType> slist_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<slist_t> ));
typedef set<MyType> set_t;
typedef multiset<MyType> multiset_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<set_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<multiset_t> ));
typedef avl_set<MyType> avl_set_t;
typedef avl_multiset<MyType> avl_multiset_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<avl_set_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<avl_multiset_t> ));
typedef bs_set<MyType> bs_set_t;
typedef bs_multiset<MyType> bs_multiset_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<bs_set_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<bs_multiset_t> ));
typedef sg_set<MyType> sg_set_t;
typedef sg_multiset<MyType> sg_multiset_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<sg_set_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<sg_multiset_t> ));
typedef treap_set<MyType> treap_set_t;
typedef treap_multiset<MyType> treap_multiset_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<treap_set_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<treap_multiset_t> ));
typedef treap_set<MyType> treap_set_t;
typedef treap_multiset<MyType> treap_multiset_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<treap_set_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<treap_multiset_t> ));
typedef unordered_set<MyType> unordered_set_t;
typedef unordered_multiset<MyType> unordered_multiset_t;
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<unordered_set_t> ));
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<unordered_multiset_t> ));
int main()
{
return 0;
}
#else
int main()
{
return 0;
}
#endif