From 529b7331499cd2491f02c4e4e68c8fc73b24dee3 Mon Sep 17 00:00:00 2001 From: Braden Ganetsky Date: Thu, 1 Jan 2026 13:41:40 -0600 Subject: [PATCH 1/3] Add missing compile-only tests in CMake --- test/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 902faf6e22..2a22d34ca4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -163,4 +163,10 @@ cfoa_tests(SOURCES cfoa/rw_spinlock_test6.cpp) cfoa_tests(SOURCES cfoa/rw_spinlock_test7.cpp) cfoa_tests(SOURCES cfoa/rw_spinlock_test8.cpp) +# Compile tests + +fca_tests(TYPE compile NAME explicit_instantiation_tests SOURCES unordered/explicit_instantiation_tests.cpp) +foa_tests(TYPE compile NAME explicit_instantiation_tests SOURCES unordered/explicit_instantiation_tests.cpp) +cfoa_tests(TYPE compile NAME explicit_instantiation_tests SOURCES cfoa/explicit_instantiation_tests.cpp) + endif() From 6732e7669f7c61241a58a648b308be2f53dfc15e Mon Sep 17 00:00:00 2001 From: Braden Ganetsky Date: Thu, 1 Jan 2026 13:35:18 -0600 Subject: [PATCH 2/3] Rewrite the FOA containers' CFOA constructors so that a class with a template conversion operator does not instantiate the CFOA container --- .../boost/unordered/unordered_flat_map.hpp | 29 ++++++-- .../boost/unordered/unordered_flat_set.hpp | 29 ++++++-- .../boost/unordered/unordered_node_map.hpp | 29 ++++++-- .../boost/unordered/unordered_node_set.hpp | 29 ++++++-- test/CMakeLists.txt | 2 + test/Jamfile.v2 | 2 + test/unordered/conversion_operator_tests.cpp | 69 +++++++++++++++++++ 7 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 test/unordered/conversion_operator_tests.cpp diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index 56ec1261ed..fcd33fe5ed 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022-2023 Christian Mazakas // Copyright (C) 2024-2025 Joaquin M Lopez Munoz +// Copyright (C) 2026 Braden Ganetsky // 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) @@ -182,10 +183,30 @@ namespace boost { { } - template - unordered_flat_map( - concurrent_flat_map&& other) - : table_(std::move(other.table_)) + template >::value, + int>::type = 0> + unordered_flat_map(U&& other) : table_(std::move(other.table_)) + { + } + + template >::value + // but we do match anything convertible to `concurrent_flat_map`. + && std::is_convertible >::value, + int>::type = 0> + unordered_flat_map(U&& other) + : unordered_flat_map( + concurrent_flat_map( + std::forward(other))) { } diff --git a/include/boost/unordered/unordered_flat_set.hpp b/include/boost/unordered/unordered_flat_set.hpp index b06a0d773c..024edf16ae 100644 --- a/include/boost/unordered/unordered_flat_set.hpp +++ b/include/boost/unordered/unordered_flat_set.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022-2023 Christian Mazakas // Copyright (C) 2024-2025 Joaquin M Lopez Munoz +// Copyright (C) 2026 Braden Ganetsky // 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) @@ -178,10 +179,30 @@ namespace boost { { } - template - unordered_flat_set( - concurrent_flat_set&& other) - : table_(std::move(other.table_)) + template >::value, + int>::type = 0> + unordered_flat_set(U&& other) : table_(std::move(other.table_)) + { + } + + template >::value + // but we do match anything convertible to `concurrent_flat_set`. + && std::is_convertible >::value, + int>::type = 0> + unordered_flat_set(U&& other) + : unordered_flat_set( + concurrent_flat_set( + std::forward(other))) { } diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index a9d7ef449c..a020717957 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022-2023 Christian Mazakas // Copyright (C) 2024-2025 Joaquin M Lopez Munoz +// Copyright (C) 2026 Braden Ganetsky // 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) @@ -189,10 +190,30 @@ namespace boost { { } - template - unordered_node_map( - concurrent_node_map&& other) - : table_(std::move(other.table_)) + template >::value, + int>::type = 0> + unordered_node_map(U&& other) : table_(std::move(other.table_)) + { + } + + template >::value + // but we do match anything convertible to `concurrent_node_map`. + && std::is_convertible >::value, + int>::type = 0> + unordered_node_map(U&& other) + : unordered_node_map( + concurrent_node_map( + std::forward(other))) { } diff --git a/include/boost/unordered/unordered_node_set.hpp b/include/boost/unordered/unordered_node_set.hpp index 1ac71b414c..0335d660b0 100644 --- a/include/boost/unordered/unordered_node_set.hpp +++ b/include/boost/unordered/unordered_node_set.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022-2023 Christian Mazakas // Copyright (C) 2024-2025 Joaquin M Lopez Munoz +// Copyright (C) 2026 Braden Ganetsky // 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) @@ -187,10 +188,30 @@ namespace boost { { } - template - unordered_node_set( - concurrent_node_set&& other) - : table_(std::move(other.table_)) + template >::value, + int>::type = 0> + unordered_node_set(U&& other) : table_(std::move(other.table_)) + { + } + + template >::value + // but we do match anything convertible to `concurrent_node_set`. + && std::is_convertible >::value, + int>::type = 0> + unordered_node_set(U&& other) + : unordered_node_set( + concurrent_node_set( + std::forward(other))) { } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2a22d34ca4..22e19f1229 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -169,4 +169,6 @@ fca_tests(TYPE compile NAME explicit_instantiation_tests SOURCES unordered/expli foa_tests(TYPE compile NAME explicit_instantiation_tests SOURCES unordered/explicit_instantiation_tests.cpp) cfoa_tests(TYPE compile NAME explicit_instantiation_tests SOURCES cfoa/explicit_instantiation_tests.cpp) +foa_tests(TYPE compile NAME conversion_operator_tests SOURCES unordered/conversion_operator_tests.cpp) + endif() diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 38ee478c36..77707deab1 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -169,6 +169,8 @@ compile unordered/explicit_instantiation_tests.cpp : compile unordered/explicit_instantiation_tests.cpp : BOOST_UNORDERED_FOA_TESTS : foa_explicit_instantiation_tests ; compile cfoa/explicit_instantiation_tests.cpp : : cfoa_explicit_instantiation_tests ; +compile unordered/conversion_operator_tests.cpp : BOOST_UNORDERED_FOA_TESTS : foa_conversion_operator_tests ; + local FCA_EXCEPTION_TESTS = constructor_exception_tests copy_exception_tests diff --git a/test/unordered/conversion_operator_tests.cpp b/test/unordered/conversion_operator_tests.cpp new file mode 100644 index 0000000000..fa2a56e268 --- /dev/null +++ b/test/unordered/conversion_operator_tests.cpp @@ -0,0 +1,69 @@ +// Copyright 2026 Braden Ganetsky +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#if !defined(BOOST_UNORDERED_FOA_TESTS) +#error "This test is only for the FOA-style conatiners" +#endif + +#include +#include +#include +#include +#include + +using flat_map = boost::unordered::unordered_flat_map; +using flat_set = boost::unordered::unordered_flat_set; +using node_map = boost::unordered::unordered_node_map; +using node_set = boost::unordered::unordered_node_set; + +struct constrained_template_converter +{ + struct dummy + { + }; + template ::value, int>::type = 0> + operator T() const + { + return T{}; + } +}; + +// Check whether the corresponding CFOA container gets instantiated +BOOST_STATIC_ASSERT( + (!std::is_constructible::value)); +BOOST_STATIC_ASSERT( + (!std::is_constructible::value)); +BOOST_STATIC_ASSERT( + (!std::is_constructible::value)); +BOOST_STATIC_ASSERT( + (!std::is_constructible::value)); + +#include +#include +#include +#include + +using c_flat_map = boost::unordered::concurrent_flat_map; +using c_flat_set = boost::unordered::concurrent_flat_set; +using c_node_map = boost::unordered::concurrent_node_map; +using c_node_set = boost::unordered::concurrent_node_set; + +template struct container_converter +{ + operator C() const { return {}; } +}; + +// Check whether the container can be constructed with an +// implicit conversion to the corresponding CFOA container +BOOST_STATIC_ASSERT( + (std::is_constructible >::value)); +BOOST_STATIC_ASSERT( + (std::is_constructible >::value)); +BOOST_STATIC_ASSERT( + (std::is_constructible >::value)); +BOOST_STATIC_ASSERT( + (std::is_constructible >::value)); + +int main() { return 0; } From 1afd508122d726379a4a8b312c8996553c9332cc Mon Sep 17 00:00:00 2001 From: Braden Ganetsky Date: Thu, 1 Jan 2026 13:50:16 -0600 Subject: [PATCH 3/3] Rewrite the CFOA containers' FOA constructors so that a class with a template conversion operator does not instantiate the FOA container --- .../boost/unordered/concurrent_flat_map.hpp | 28 ++++++-- .../boost/unordered/concurrent_flat_set.hpp | 27 ++++++-- .../boost/unordered/concurrent_node_map.hpp | 29 +++++++-- .../boost/unordered/concurrent_node_set.hpp | 28 ++++++-- test/CMakeLists.txt | 1 + test/Jamfile.v2 | 1 + test/cfoa/conversion_operator_tests.cpp | 65 +++++++++++++++++++ 7 files changed, 163 insertions(+), 16 deletions(-) create mode 100644 test/cfoa/conversion_operator_tests.cpp diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index 77445d1878..b46a759399 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -2,6 +2,7 @@ * * Copyright 2023 Christian Mazakas. * Copyright 2023-2024 Joaquin M Lopez Munoz. + * Copyright 2026 Braden Ganetsky * 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) @@ -189,11 +190,30 @@ namespace boost { { } + template >::value, + int>::type = 0> + concurrent_flat_map(U&& other) : table_(std::move(other.table_)) + { + } - template - concurrent_flat_map( - unordered_flat_map&& other) - : table_(std::move(other.table_)) + template >::value + // but we do match anything convertible to `unordered_flat_map`. + && std::is_convertible >::value, + int>::type = 0> + concurrent_flat_map(U&& other) + : concurrent_flat_map( + unordered_flat_map( + std::forward(other))) { } diff --git a/include/boost/unordered/concurrent_flat_set.hpp b/include/boost/unordered/concurrent_flat_set.hpp index f0f44308e4..514b90e100 100644 --- a/include/boost/unordered/concurrent_flat_set.hpp +++ b/include/boost/unordered/concurrent_flat_set.hpp @@ -2,6 +2,7 @@ * * Copyright 2023 Christian Mazakas. * Copyright 2023-2024 Joaquin M Lopez Munoz. + * Copyright 2026 Braden Ganetsky * 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) @@ -186,11 +187,29 @@ namespace boost { { } + template >::value, + int>::type = 0> + concurrent_flat_set(U&& other) : table_(std::move(other.table_)) + { + } - template - concurrent_flat_set( - unordered_flat_set&& other) - : table_(std::move(other.table_)) + template >::value + // but we do match anything convertible to `unordered_flat_set`. + && std::is_convertible >::value, + int>::type = 0> + concurrent_flat_set(U&& other) + : concurrent_flat_set(unordered_flat_set( + std::forward(other))) { } diff --git a/include/boost/unordered/concurrent_node_map.hpp b/include/boost/unordered/concurrent_node_map.hpp index 1b6f1623a0..5ea7f24a8b 100644 --- a/include/boost/unordered/concurrent_node_map.hpp +++ b/include/boost/unordered/concurrent_node_map.hpp @@ -2,6 +2,7 @@ * * Copyright 2023 Christian Mazakas. * Copyright 2023-2024 Joaquin M Lopez Munoz. + * Copyright 2026 Braden Ganetsky * 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) @@ -197,10 +198,30 @@ namespace boost { { } - template - concurrent_node_map( - unordered_node_map&& other) - : table_(std::move(other.table_)) + template >::value, + int>::type = 0> + concurrent_node_map(U&& other) : table_(std::move(other.table_)) + { + } + + template >::value + // but we do match anything convertible to `unordered_node_map`. + && std::is_convertible >::value, + int>::type = 0> + concurrent_node_map(U&& other) + : concurrent_node_map( + unordered_node_map( + std::forward(other))) { } diff --git a/include/boost/unordered/concurrent_node_set.hpp b/include/boost/unordered/concurrent_node_set.hpp index 430bc0798c..bb8865d0dc 100644 --- a/include/boost/unordered/concurrent_node_set.hpp +++ b/include/boost/unordered/concurrent_node_set.hpp @@ -2,6 +2,7 @@ * * Copyright 2023 Christian Mazakas. * Copyright 2023-2024 Joaquin M Lopez Munoz. + * Copyright 2026 Braden Ganetsky * 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) @@ -194,10 +195,29 @@ namespace boost { { } - template - concurrent_node_set( - unordered_node_set&& other) - : table_(std::move(other.table_)) + template >::value, + int>::type = 0> + concurrent_node_set(U&& other) : table_(std::move(other.table_)) + { + } + + template >::value + // but we do match anything convertible to `unordered_node_set`. + && std::is_convertible >::value, + int>::type = 0> + concurrent_node_set(U&& other) + : concurrent_node_set(unordered_node_set( + std::forward(other))) { } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 22e19f1229..81f1e8100f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -170,5 +170,6 @@ foa_tests(TYPE compile NAME explicit_instantiation_tests SOURCES unordered/expli cfoa_tests(TYPE compile NAME explicit_instantiation_tests SOURCES cfoa/explicit_instantiation_tests.cpp) foa_tests(TYPE compile NAME conversion_operator_tests SOURCES unordered/conversion_operator_tests.cpp) +cfoa_tests(TYPE compile NAME conversion_operator_tests SOURCES cfoa/conversion_operator_tests.cpp) endif() diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 77707deab1..8db539e63a 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -170,6 +170,7 @@ compile unordered/explicit_instantiation_tests.cpp : BOOST_UNORDERED_FOA compile cfoa/explicit_instantiation_tests.cpp : : cfoa_explicit_instantiation_tests ; compile unordered/conversion_operator_tests.cpp : BOOST_UNORDERED_FOA_TESTS : foa_conversion_operator_tests ; +compile cfoa/conversion_operator_tests.cpp : : cfoa_conversion_operator_tests ; local FCA_EXCEPTION_TESTS = constructor_exception_tests diff --git a/test/cfoa/conversion_operator_tests.cpp b/test/cfoa/conversion_operator_tests.cpp new file mode 100644 index 0000000000..b8a32b79ee --- /dev/null +++ b/test/cfoa/conversion_operator_tests.cpp @@ -0,0 +1,65 @@ +// Copyright 2026 Braden Ganetsky +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include + +using c_flat_map = boost::unordered::concurrent_flat_map; +using c_flat_set = boost::unordered::concurrent_flat_set; +using c_node_map = boost::unordered::concurrent_node_map; +using c_node_set = boost::unordered::concurrent_node_set; + +struct constrained_template_converter +{ + struct dummy + { + }; + template ::value, int>::type = 0> + operator T() const + { + return T{}; + } +}; + +// Check whether the corresponding FOA container gets instantiated +BOOST_STATIC_ASSERT( + (!std::is_constructible::value)); +BOOST_STATIC_ASSERT( + (!std::is_constructible::value)); +BOOST_STATIC_ASSERT( + (!std::is_constructible::value)); +BOOST_STATIC_ASSERT( + (!std::is_constructible::value)); + +#include +#include +#include +#include + +using flat_map = boost::unordered::unordered_flat_map; +using flat_set = boost::unordered::unordered_flat_set; +using node_map = boost::unordered::unordered_node_map; +using node_set = boost::unordered::unordered_node_set; + +template struct container_converter +{ + operator C() const { return {}; } +}; + +// Check whether the container can be constructed with an +// implicit conversion to the corresponding FOA container +BOOST_STATIC_ASSERT( + (std::is_constructible >::value)); +BOOST_STATIC_ASSERT( + (std::is_constructible >::value)); +BOOST_STATIC_ASSERT( + (std::is_constructible >::value)); +BOOST_STATIC_ASSERT( + (std::is_constructible >::value)); + +int main() { return 0; }