Skip to content

Commit 410f7a6

Browse files
committed
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`.
1 parent 2d25ad5 commit 410f7a6

File tree

8 files changed

+243
-4
lines changed

8 files changed

+243
-4
lines changed

doc/intrusive.qbk

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3905,6 +3905,19 @@ to be inserted in intrusive containers are allocated using `std::vector` or `std
39053905

39063906
[section:release_notes Release Notes]
39073907

3908+
[section:release_notes_boost_1_91_00 Boost 1.91 Release]
3909+
3910+
* Hooks and containers using `normal_link`s now have defaulted destructors if C++20 concepts are available. This allows
3911+
trivially destructible hooks and containers if internally used nodes and comparison objects are trivially destructible.
3912+
3913+
* Hooks using `normal_link`s now have defaulted constructors if C++20 concepts are available. This allows
3914+
trivially default constructible hooks if internally used nodes are trivially destructible.
3915+
3916+
* Fixed bugs:
3917+
* [@https://github.com/boostorg/intrusive/issues/99 GitHub #92: ['some intrusive containers are not trivially destructible when possible]]
3918+
3919+
[endsect]
3920+
39083921
[section:release_notes_boost_1_89_00 Boost 1.89 Release]
39093922

39103923
* Fixed bugs:

include/boost/intrusive/bstree.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,9 @@ struct bstbase
585585
//Detach all inserted nodes. This will add exception safety to bstree_impl
586586
//constructors inserting elements.
587587
~bstbase()
588+
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
589+
requires (ValueTraits::link_mode != normal_link)
590+
#endif
588591
{
589592
if(is_safe_autounlink<value_traits::link_mode>::value){
590593
node_algorithms::clear_and_dispose
@@ -594,6 +597,11 @@ struct bstbase
594597
node_algorithms::init(this->header_ptr());
595598
}
596599
}
600+
601+
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
602+
//Default destructor for normal links (allows conditional triviality)
603+
~bstbase() requires (ValueTraits::link_mode == normal_link) = default;
604+
#endif
597605
};
598606

599607

include/boost/intrusive/detail/generic_hook.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,20 @@ class generic_hook
170170
/// @endcond
171171

172172
inline generic_hook() BOOST_NOEXCEPT
173+
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
174+
requires (LinkMode != normal_link)
175+
#endif
173176
{
174177
if(hooktags::safemode_or_autounlink){
175178
node_algorithms::init(this->this_ptr());
176179
}
177180
}
178181

182+
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
183+
//Default destructor for normal links (allows conditional triviality)
184+
generic_hook() requires (LinkMode == normal_link) = default;
185+
#endif
186+
179187
inline generic_hook(const generic_hook& ) BOOST_NOEXCEPT
180188
{
181189
if(hooktags::safemode_or_autounlink){
@@ -187,11 +195,19 @@ class generic_hook
187195
{ return *this; }
188196

189197
inline ~generic_hook()
198+
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
199+
requires (LinkMode != normal_link)
200+
#endif
190201
{
191202
destructor_impl
192203
(*this, detail::link_dispatch<hooktags::link_mode>());
193204
}
194205

206+
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
207+
//Default destructor for normal links (allows conditional triviality)
208+
~generic_hook() requires (LinkMode == normal_link) = default;
209+
#endif
210+
195211
inline void swap_nodes(generic_hook &other) BOOST_NOEXCEPT
196212
{
197213
node_algorithms::swap_nodes

include/boost/intrusive/detail/workaround.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,8 @@ template<unsigned> struct static_assert_test {};
121121
# define BOOST_INTRUSIVE_NO_DANGLING
122122
#endif
123123

124+
#if defined(__cpp_concepts) && (__cpp_concepts >= 202002L)
125+
# define BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING
126+
#endif
124127

125128
#endif //#ifndef BOOST_INTRUSIVE_DETAIL_WORKAROUND_HPP

include/boost/intrusive/hashtable.hpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,7 +1909,19 @@ struct hashdata_internal
19091909
{ return this->priv_size_traits(); }
19101910

19111911
~hashdata_internal()
1912-
{ this->priv_clear_buckets(); }
1912+
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
1913+
requires (ValueTraits::link_mode != normal_link)
1914+
#endif
1915+
{
1916+
BOOST_IF_CONSTEXPR(safemode_or_autounlink){
1917+
this->priv_clear_buckets();
1918+
}
1919+
}
1920+
1921+
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
1922+
//Default destructor for normal links (allows conditional triviality)
1923+
~hashdata_internal() requires (ValueTraits::link_mode == normal_link) = default;
1924+
#endif
19131925

19141926
using split_bucket_hash_equal_t::priv_clear_buckets;
19151927

@@ -2462,17 +2474,16 @@ class hashtable_impl
24622474
hashtable_impl& operator=(BOOST_RV_REF(hashtable_impl) x)
24632475
{ this->swap(x); return *this; }
24642476

2477+
#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
24652478
//! <b>Effects</b>: Detaches all elements from this. The objects in the unordered_set
24662479
//! are not deleted (i.e. no destructors are called).
24672480
//!
24682481
//! <b>Complexity</b>: Linear to the number of elements in the unordered_set, if
24692482
//! it's a safe-mode or auto-unlink value. Otherwise constant.
24702483
//!
24712484
//! <b>Throws</b>: Nothing.
2472-
~hashtable_impl()
2473-
{}
2485+
~hashtable_impl();
24742486

2475-
#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
24762487
//! <b>Effects</b>: Returns an iterator pointing to the beginning of the unordered_set.
24772488
//!
24782489
//! <b>Complexity</b>: Amortized constant time.

include/boost/intrusive/list.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,21 @@ class list_impl
249249
//! <b>Complexity</b>: Linear to the number of elements in the list, if
250250
//! it's a safe-mode or auto-unlink value . Otherwise constant.
251251
~list_impl()
252+
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
253+
requires (ValueTraits::link_mode != normal_link)
254+
#endif
252255
{
253256
BOOST_IF_CONSTEXPR(is_safe_autounlink<ValueTraits::link_mode>::value){
254257
this->clear();
255258
node_algorithms::init(this->get_root_node());
256259
}
257260
}
258261

262+
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
263+
//Default destructor for normal links (allows conditional triviality)
264+
~list_impl() requires (ValueTraits::link_mode == normal_link) = default;
265+
#endif
266+
259267
//! <b>Requires</b>: value must be an lvalue.
260268
//!
261269
//! <b>Effects</b>: Inserts the value in the back of the list.

include/boost/intrusive/slist.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,13 +367,21 @@ class slist_impl
367367
//! <b>Complexity</b>: Linear to the number of elements in the list, if
368368
//! it's a safe-mode or auto-unlink value. Otherwise constant.
369369
~slist_impl()
370+
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
371+
requires (ValueTraits::link_mode != normal_link)
372+
#endif
370373
{
371374
BOOST_IF_CONSTEXPR(is_safe_autounlink<ValueTraits::link_mode>::value){
372375
this->clear();
373376
node_algorithms::init(this->get_root_node());
374377
}
375378
}
376379

380+
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) && defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
381+
//Default destructor for normal links (allows conditional triviality)
382+
~slist_impl() requires (ValueTraits::link_mode == normal_link) = default;
383+
#endif
384+
377385
//! <b>Effects</b>: Erases all the elements of the container.
378386
//!
379387
//! <b>Throws</b>: Nothing.

test/trivial_destructor_test.cpp

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/////////////////////////////////////////////////////////////////////////////
2+
//
3+
// (C) Copyright Ion Gaztanaga 2025-2025
4+
//
5+
// Distributed under the Boost Software License, Version 1.0.
6+
// (See accompanying file LICENSE_1_0.txt or copy at
7+
// http://www.boost.org/LICENSE_1_0.txt)
8+
//
9+
// See http://www.boost.org/libs/intrusive for documentation.
10+
//
11+
/////////////////////////////////////////////////////////////////////////////
12+
13+
#include <boost/config.hpp>
14+
15+
//Conditional triviality is based on destructor overloads based on concepts
16+
#if defined(BOOST_INTRUSIVE_CONCEPTS_BASED_OVERLOADING)
17+
18+
#include <type_traits>
19+
20+
#include <boost/intrusive/list.hpp>
21+
#include <boost/intrusive/slist.hpp>
22+
#include <boost/intrusive/set.hpp>
23+
#include <boost/intrusive/unordered_set.hpp>
24+
#include <boost/intrusive/splay_set.hpp>
25+
#include <boost/intrusive/avl_set.hpp>
26+
#include <boost/intrusive/sg_set.hpp>
27+
#include <boost/intrusive/treap_set.hpp>
28+
#include <boost/intrusive/bs_set.hpp>
29+
#include <boost/intrusive/pointer_traits.hpp>
30+
#include <boost/intrusive/any_hook.hpp>
31+
32+
using namespace boost::intrusive;
33+
34+
typedef list_base_hook
35+
< void_pointer<void*>, link_mode<normal_link> > list_base_hook_t;
36+
typedef slist_base_hook
37+
< void_pointer<void*>, link_mode<normal_link> > slist_base_hook_t;
38+
typedef set_base_hook
39+
< void_pointer<void*>, link_mode<normal_link> > set_base_hook_t;
40+
typedef avl_set_base_hook
41+
< void_pointer<void*>, link_mode<normal_link> > avl_base_hook_t;
42+
typedef bs_set_base_hook
43+
< void_pointer<void*>, link_mode<normal_link> > bs_base_hook_t;
44+
typedef unordered_set_base_hook
45+
< void_pointer<void*>, link_mode<normal_link> > unordered_base_hook_t;
46+
typedef any_base_hook
47+
< void_pointer<void*>, link_mode<normal_link> > any_base_hook_t;
48+
49+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<list_base_hook_t> ));
50+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<slist_base_hook_t> ));
51+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<set_base_hook_t> ));
52+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<avl_base_hook_t> ));
53+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<bs_base_hook_t> ));
54+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<unordered_base_hook_t> ));
55+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<any_base_hook_t> ));
56+
57+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<list_base_hook_t> ));
58+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<slist_base_hook_t> ));
59+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<set_base_hook_t> ));
60+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<avl_base_hook_t> ));
61+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<bs_base_hook_t> ));
62+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<unordered_base_hook_t> ));
63+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<any_base_hook_t> ));
64+
65+
typedef list_member_hook
66+
< void_pointer<void*>, link_mode<normal_link> > list_member_hook_t;
67+
typedef slist_member_hook
68+
< void_pointer<void*>, link_mode<normal_link> > slist_member_hook_t;
69+
typedef set_member_hook
70+
< void_pointer<void*>, link_mode<normal_link> > set_member_hook_t;
71+
typedef avl_set_member_hook
72+
< void_pointer<void*>, link_mode<normal_link> > avl_member_hook_t;
73+
typedef bs_set_member_hook
74+
< void_pointer<void*>, link_mode<normal_link> > bs_member_hook_t;
75+
typedef unordered_set_member_hook
76+
< void_pointer<void*>, link_mode<normal_link> > unordered_member_hook_t;
77+
typedef any_member_hook
78+
< void_pointer<void*>, link_mode<normal_link> > any_member_hook_t;
79+
80+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<list_member_hook_t> ));
81+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<slist_member_hook_t> ));
82+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<set_member_hook_t> ));
83+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<avl_member_hook_t> ));
84+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<bs_member_hook_t> ));
85+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<unordered_member_hook_t> ));
86+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<any_member_hook_t> ));
87+
88+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<list_member_hook_t> ));
89+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<slist_member_hook_t> ));
90+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<set_member_hook_t> ));
91+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<avl_member_hook_t> ));
92+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<bs_member_hook_t> ));
93+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<unordered_member_hook_t> ));
94+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_default_constructible_v<any_member_hook_t> ));
95+
96+
struct MyType
97+
: public list_base_hook_t
98+
, public slist_base_hook_t
99+
, public set_base_hook_t
100+
, public avl_base_hook_t
101+
, public bs_base_hook_t
102+
, public unordered_base_hook_t
103+
, public any_base_hook_t
104+
{
105+
list_member_hook_t limh;
106+
slist_member_hook_t slmh;
107+
set_member_hook_t semh;
108+
avl_member_hook_t avmh;
109+
bs_member_hook_t bsmh;
110+
unordered_member_hook_t unmh;
111+
any_member_hook_t anmh;
112+
113+
friend bool operator<(const MyType &, const MyType &) { return true; }
114+
friend std::size_t hash_value(const MyType &, const MyType &) { return 0u; }
115+
};
116+
117+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<MyType> ));
118+
119+
typedef list<MyType> list_t;
120+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<list_t> ));
121+
122+
typedef slist<MyType> slist_t;
123+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<slist_t> ));
124+
125+
typedef set<MyType> set_t;
126+
typedef multiset<MyType> multiset_t;
127+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<set_t> ));
128+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<multiset_t> ));
129+
130+
typedef avl_set<MyType> avl_set_t;
131+
typedef avl_multiset<MyType> avl_multiset_t;
132+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<avl_set_t> ));
133+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<avl_multiset_t> ));
134+
135+
typedef bs_set<MyType> bs_set_t;
136+
typedef bs_multiset<MyType> bs_multiset_t;
137+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<bs_set_t> ));
138+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<bs_multiset_t> ));
139+
140+
typedef sg_set<MyType> sg_set_t;
141+
typedef sg_multiset<MyType> sg_multiset_t;
142+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<sg_set_t> ));
143+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<sg_multiset_t> ));
144+
145+
typedef treap_set<MyType> treap_set_t;
146+
typedef treap_multiset<MyType> treap_multiset_t;
147+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<treap_set_t> ));
148+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<treap_multiset_t> ));
149+
150+
typedef treap_set<MyType> treap_set_t;
151+
typedef treap_multiset<MyType> treap_multiset_t;
152+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<treap_set_t> ));
153+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<treap_multiset_t> ));
154+
155+
typedef unordered_set<MyType> unordered_set_t;
156+
typedef unordered_multiset<MyType> unordered_multiset_t;
157+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<unordered_set_t> ));
158+
BOOST_INTRUSIVE_STATIC_ASSERT(( std::is_trivially_destructible_v<unordered_multiset_t> ));
159+
160+
int main()
161+
{
162+
return 0;
163+
}
164+
165+
#else
166+
167+
int main()
168+
{
169+
return 0;
170+
}
171+
172+
#endif

0 commit comments

Comments
 (0)