Skip to content

Commit 5c7cb71

Browse files
committed
Rewrite the FOA containers' CFOA constructors so that a class with a template conversion operator does not instantiate the CFOA container
1 parent 16a5dd1 commit 5c7cb71

7 files changed

Lines changed: 172 additions & 16 deletions

File tree

include/boost/unordered/unordered_flat_map.hpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (C) 2022-2023 Christian Mazakas
22
// Copyright (C) 2024-2025 Joaquin M Lopez Munoz
3+
// Copyright (C) 2026 Braden Ganetsky
34
// Distributed under the Boost Software License, Version 1.0. (See accompanying
45
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
56

@@ -182,10 +183,30 @@ namespace boost {
182183
{
183184
}
184185

185-
template <bool avoid_explicit_instantiation = true>
186-
unordered_flat_map(
187-
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator>&& other)
188-
: table_(std::move(other.table_))
186+
template <class U,
187+
typename std::enable_if<
188+
// Ensure we match exactly `concurrent_flat_map&&`.
189+
// Any lvalue references to `concurrent_flat_map` are not supported.
190+
std::is_same<U,
191+
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> >::value,
192+
int>::type = 0>
193+
unordered_flat_map(U&& other) : table_(std::move(other.table_))
194+
{
195+
}
196+
197+
template <class U,
198+
typename std::enable_if<
199+
// Ensure we don't match any cvref-qualified `concurrent_flat_map&&`,
200+
!detail::is_similar<U,
201+
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> >::value
202+
// but we do match anything convertible to `concurrent_flat_map`.
203+
&& std::is_convertible<U&&, concurrent_flat_map<Key, T, Hash,
204+
KeyEqual, Allocator> >::value,
205+
int>::type = 0>
206+
unordered_flat_map(U&& other)
207+
: unordered_flat_map(
208+
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator>(
209+
std::forward<U>(other)))
189210
{
190211
}
191212

include/boost/unordered/unordered_flat_set.hpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (C) 2022-2023 Christian Mazakas
22
// Copyright (C) 2024-2025 Joaquin M Lopez Munoz
3+
// Copyright (C) 2026 Braden Ganetsky
34
// Distributed under the Boost Software License, Version 1.0. (See accompanying
45
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
56

@@ -178,10 +179,30 @@ namespace boost {
178179
{
179180
}
180181

181-
template <bool avoid_explicit_instantiation = true>
182-
unordered_flat_set(
183-
concurrent_flat_set<Key, Hash, KeyEqual, Allocator>&& other)
184-
: table_(std::move(other.table_))
182+
template <class U,
183+
typename std::enable_if<
184+
// Ensure we match exactly `concurrent_flat_set&&`.
185+
// Any lvalue references to `concurrent_flat_set` are not supported.
186+
std::is_same<U,
187+
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> >::value,
188+
int>::type = 0>
189+
unordered_flat_set(U&& other) : table_(std::move(other.table_))
190+
{
191+
}
192+
193+
template <class U,
194+
typename std::enable_if<
195+
// Ensure we don't match any cvref-qualified `concurrent_flat_set&&`,
196+
!detail::is_similar<U,
197+
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> >::value
198+
// but we do match anything convertible to `concurrent_flat_set`.
199+
&& std::is_convertible<U&&,
200+
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> >::value,
201+
int>::type = 0>
202+
unordered_flat_set(U&& other)
203+
: unordered_flat_set(
204+
concurrent_flat_set<Key, Hash, KeyEqual, Allocator>(
205+
std::forward<U>(other)))
185206
{
186207
}
187208

include/boost/unordered/unordered_node_map.hpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (C) 2022-2023 Christian Mazakas
22
// Copyright (C) 2024-2025 Joaquin M Lopez Munoz
3+
// Copyright (C) 2026 Braden Ganetsky
34
// Distributed under the Boost Software License, Version 1.0. (See accompanying
45
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
56

@@ -189,10 +190,30 @@ namespace boost {
189190
{
190191
}
191192

192-
template <bool avoid_explicit_instantiation = true>
193-
unordered_node_map(
194-
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator>&& other)
195-
: table_(std::move(other.table_))
193+
template <class U,
194+
typename std::enable_if<
195+
// Ensure we match exactly `concurrent_node_map&&`.
196+
// Any lvalue references to `concurrent_node_map` are not supported.
197+
std::is_same<U,
198+
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> >::value,
199+
int>::type = 0>
200+
unordered_node_map(U&& other) : table_(std::move(other.table_))
201+
{
202+
}
203+
204+
template <class U,
205+
typename std::enable_if<
206+
// Ensure we don't match any cvref-qualified `concurrent_node_map&&`,
207+
!detail::is_similar<U,
208+
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> >::value
209+
// but we do match anything convertible to `concurrent_node_map`.
210+
&& std::is_convertible<U&&, concurrent_node_map<Key, T, Hash,
211+
KeyEqual, Allocator> >::value,
212+
int>::type = 0>
213+
unordered_node_map(U&& other)
214+
: unordered_node_map(
215+
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator>(
216+
std::forward<U>(other)))
196217
{
197218
}
198219

include/boost/unordered/unordered_node_set.hpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (C) 2022-2023 Christian Mazakas
22
// Copyright (C) 2024-2025 Joaquin M Lopez Munoz
3+
// Copyright (C) 2026 Braden Ganetsky
34
// Distributed under the Boost Software License, Version 1.0. (See accompanying
45
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
56

@@ -187,10 +188,30 @@ namespace boost {
187188
{
188189
}
189190

190-
template <bool avoid_explicit_instantiation = true>
191-
unordered_node_set(
192-
concurrent_node_set<Key, Hash, KeyEqual, Allocator>&& other)
193-
: table_(std::move(other.table_))
191+
template <class U,
192+
typename std::enable_if<
193+
// Ensure we match exactly `concurrent_node_set&&`.
194+
// Any lvalue references to `concurrent_node_set` are not supported.
195+
std::is_same<U,
196+
concurrent_node_set<Key, Hash, KeyEqual, Allocator> >::value,
197+
int>::type = 0>
198+
unordered_node_set(U&& other) : table_(std::move(other.table_))
199+
{
200+
}
201+
202+
template <class U,
203+
typename std::enable_if<
204+
// Ensure we don't match any cvref-qualified `concurrent_node_set&&`,
205+
!detail::is_similar<U,
206+
concurrent_node_set<Key, Hash, KeyEqual, Allocator> >::value
207+
// but we do match anything convertible to `concurrent_node_set`.
208+
&& std::is_convertible<U&&,
209+
concurrent_node_set<Key, Hash, KeyEqual, Allocator> >::value,
210+
int>::type = 0>
211+
unordered_node_set(U&& other)
212+
: unordered_node_set(
213+
concurrent_node_set<Key, Hash, KeyEqual, Allocator>(
214+
std::forward<U>(other)))
194215
{
195216
}
196217

test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ fca_tests(TYPE compile NAME fca_explicit_instantiation_tests SOURCES unordered/e
8888
fca_tests(TYPE compile NAME foa_explicit_instantiation_tests COMPILE_DEFINITIONS BOOST_UNORDERED_FOA_TESTS SOURCES unordered/explicit_instantiation_tests.cpp)
8989
fca_tests(TYPE compile NAME cfoa_explicit_instantiation_tests SOURCES cfoa/explicit_instantiation_tests.cpp)
9090

91+
fca_tests(TYPE compile NAME foa_conversion_operator_tests COMPILE_DEFINITIONS BOOST_UNORDERED_FOA_TESTS SOURCES unordered/conversion_operator_tests.cpp)
92+
9193
# FOA tests
9294

9395
foa_tests(SOURCES unordered/fwd_set_test.cpp)

test/Jamfile.v2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ compile unordered/explicit_instantiation_tests.cpp :
169169
compile unordered/explicit_instantiation_tests.cpp : <define>BOOST_UNORDERED_FOA_TESTS : foa_explicit_instantiation_tests ;
170170
compile cfoa/explicit_instantiation_tests.cpp : : cfoa_explicit_instantiation_tests ;
171171

172+
compile unordered/conversion_operator_tests.cpp : <define>BOOST_UNORDERED_FOA_TESTS : foa_conversion_operator_tests ;
173+
172174
local FCA_EXCEPTION_TESTS =
173175
constructor_exception_tests
174176
copy_exception_tests
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2026 Braden Ganetsky
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#if !defined(BOOST_UNORDERED_FOA_TESTS)
6+
#error "This test is only for the FOA-style conatiners"
7+
#endif
8+
9+
#include <boost/unordered/unordered_flat_map.hpp>
10+
#include <boost/unordered/unordered_flat_set.hpp>
11+
#include <boost/unordered/unordered_node_map.hpp>
12+
#include <boost/unordered/unordered_node_set.hpp>
13+
14+
using flat_map = boost::unordered::unordered_flat_map<int, int>;
15+
using flat_set = boost::unordered::unordered_flat_set<int>;
16+
using node_map = boost::unordered::unordered_node_map<int, int>;
17+
using node_set = boost::unordered::unordered_node_set<int>;
18+
19+
struct constrained_template_converter
20+
{
21+
struct dummy
22+
{
23+
};
24+
template <class T, typename std::enable_if<
25+
std::is_constructible<T, dummy>::value, int>::type = 0>
26+
operator T() const
27+
{
28+
return T{};
29+
}
30+
};
31+
32+
// Check whether the corresponding CFOA container gets instantiated
33+
static_assert(
34+
!std::is_constructible<flat_map, constrained_template_converter>::value);
35+
static_assert(
36+
!std::is_constructible<flat_set, constrained_template_converter>::value);
37+
static_assert(
38+
!std::is_constructible<node_map, constrained_template_converter>::value);
39+
static_assert(
40+
!std::is_constructible<node_set, constrained_template_converter>::value);
41+
42+
#include <boost/unordered/concurrent_flat_map.hpp>
43+
#include <boost/unordered/concurrent_flat_set.hpp>
44+
#include <boost/unordered/concurrent_node_map.hpp>
45+
#include <boost/unordered/concurrent_node_set.hpp>
46+
47+
using c_flat_map = boost::unordered::concurrent_flat_map<int, int>;
48+
using c_flat_set = boost::unordered::concurrent_flat_set<int>;
49+
using c_node_map = boost::unordered::concurrent_node_map<int, int>;
50+
using c_node_set = boost::unordered::concurrent_node_set<int>;
51+
52+
template <class C> struct container_converter
53+
{
54+
operator C() const { return {}; }
55+
};
56+
57+
// Check whether the container can be constructed with an
58+
// implicit conversion to the corresponding CFOA container
59+
static_assert(
60+
std::is_constructible<flat_map, container_converter<c_flat_map> >::value);
61+
static_assert(
62+
std::is_constructible<flat_set, container_converter<c_flat_set> >::value);
63+
static_assert(
64+
std::is_constructible<node_map, container_converter<c_node_map> >::value);
65+
static_assert(
66+
std::is_constructible<node_set, container_converter<c_node_set> >::value);
67+
68+
int main() { return 0; }

0 commit comments

Comments
 (0)