From e328b75387e9bd0157d4b398ea6af6397ded34fe Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 12:37:33 +0200 Subject: [PATCH 01/10] constrained relational operators, P2944R3-style --- include/boost/variant2/variant.hpp | 60 +++++++++- test/Jamfile | 2 + test/variant_constrained_relops.cpp | 176 ++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 6 deletions(-) create mode 100644 test/variant_constrained_relops.cpp diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index bb94c27..9f55bd4 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -2103,6 +2103,21 @@ template class variant: private detail::variant_ma_base namespace detail { +namespace relops_constraints +{ + +struct not_comparable {}; +template not_comparable operator==( U const &, U const & ); +template not_comparable operator!=( U const &, U const & ); +template not_comparable operator< ( U const &, U const & ); +template not_comparable operator<=( U const &, U const & ); + +template struct has_eq: std::is_convertible() == std::declval() ), bool> +{ +}; + +} // namespace relops_constraints + template struct eq_L { variant const & v; @@ -2116,7 +2131,8 @@ template struct eq_L } // namespace detail -template constexpr bool operator==( variant const & v, variant const & w ) +template...>::value>::type> +constexpr bool operator==( variant const & v, variant const & w ) { return v.index() == w.index() && mp11::mp_with_index( v.index(), detail::eq_L{ v, w } ); } @@ -2124,6 +2140,15 @@ template constexpr bool operator==( variant const & v, variant namespace detail { +namespace relops_constraints +{ + +template struct has_neq: std::is_convertible() != std::declval() ), bool> +{ +}; + +} // namespace relops_constraints + template struct ne_L { variant const & v; @@ -2137,7 +2162,8 @@ template struct ne_L } // namespace detail -template constexpr bool operator!=( variant const & v, variant const & w ) +template...>::value>::type> +constexpr bool operator!=( variant const & v, variant const & w ) { return v.index() != w.index() || mp11::mp_with_index( v.index(), detail::ne_L{ v, w } ); } @@ -2145,6 +2171,15 @@ template constexpr bool operator!=( variant const & v, variant namespace detail { +namespace relops_constraints +{ + +template struct has_lt: std::is_convertible() < std::declval() ), bool> +{ +}; + +} // namespace relops_constraints + template struct lt_L { variant const & v; @@ -2158,12 +2193,14 @@ template struct lt_L } // namespace detail -template constexpr bool operator<( variant const & v, variant const & w ) +template...>::value>::type> +constexpr bool operator<( variant const & v, variant const & w ) { return v.index() < w.index() || ( v.index() == w.index() && mp11::mp_with_index( v.index(), detail::lt_L{ v, w } ) ); } -template constexpr bool operator>( variant const & v, variant const & w ) +template...>::value>::type> +constexpr bool operator>( variant const & v, variant const & w ) { return w < v; } @@ -2171,6 +2208,15 @@ template constexpr bool operator>( variant const & v, variant namespace detail { +namespace relops_constraints +{ + +template struct has_le: std::is_convertible() <= std::declval() ), bool> +{ +}; + +} // namespace relops_constraints + template struct le_L { variant const & v; @@ -2184,12 +2230,14 @@ template struct le_L } // namespace detail -template constexpr bool operator<=( variant const & v, variant const & w ) +template...>::value>::type> +constexpr bool operator<=( variant const & v, variant const & w ) { return v.index() < w.index() || ( v.index() == w.index() && mp11::mp_with_index( v.index(), detail::le_L{ v, w } ) ); } -template constexpr bool operator>=( variant const & v, variant const & w ) +template...>::value>::type> +constexpr bool operator>=( variant const & v, variant const & w ) { return w <= v; } diff --git a/test/Jamfile b/test/Jamfile index 4d88ebf..d8c3a77 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -180,3 +180,5 @@ run variant_issue_55.cpp # clang-cl 32 bit fails with an assertion in mp_with_index, likely due to a codegen bug "clang-win,32:no" ; + +run variant_constrained_relops.cpp ; diff --git a/test/variant_constrained_relops.cpp b/test/variant_constrained_relops.cpp new file mode 100644 index 0000000..1fd9c18 --- /dev/null +++ b/test/variant_constrained_relops.cpp @@ -0,0 +1,176 @@ + +// Copyright 2026 Joaquin M Lopez Munoz. +// +// 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 + +#include +#include +#include +#include + +using namespace boost::variant2; + +struct not_found {}; +struct any { any( ... ); }; +not_found operator==( any const &, any const & ); +not_found operator!=( any const &, any const & ); +not_found operator< ( any const &, any const & ); +not_found operator<=( any const &, any const & ); +not_found operator> ( any const &, any const & ); +not_found operator>=( any const &, any const & ); + +struct no_eq +{ + int operator!=( const no_eq& ) const; + int operator< ( const no_eq& ) const; + int operator<=( const no_eq& ) const; +}; + +struct bad_eq +{ + void operator==( const bad_eq& ) const; + int operator!=( const bad_eq& ) const; + int operator< ( const bad_eq& ) const; + int operator<=( const bad_eq& ) const; +}; + +struct no_neq +{ + int operator==( const no_neq& ) const; + int operator< ( const no_neq& ) const; + int operator<=( const no_neq& ) const; +}; + +struct bad_neq +{ + int operator==( const bad_neq& ) const; + void operator!=( const bad_neq& ) const; + int operator< ( const bad_neq& ) const; + int operator<=( const bad_neq& ) const; +}; + +struct no_lt +{ + int operator==( const no_lt& ) const; + int operator!=( const no_lt& ) const; + int operator<=( const no_lt& ) const; +}; + +struct bad_lt +{ + int operator==( const bad_lt& ) const; + int operator!=( const bad_lt& ) const; + void operator< ( const bad_lt& ) const; + int operator<=( const bad_lt& ) const; +}; + +struct no_le +{ + int operator==( const no_le& ) const; + int operator!=( const no_le& ) const; + int operator< ( const no_le& ) const; +}; + +struct bad_le +{ + int operator==( const bad_le& ) const; + int operator!=( const bad_le& ) const; + int operator< ( const bad_le& ) const; + void operator<=( const bad_le& ) const; +}; + +int main() +{ + { + using v_t = const variant&; + + BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), bool >)); + } + + { + using v_t = const variant&; + + BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), bool >)); + } + + { + using v_t = const variant&; + + BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), bool >)); + } + + { + using v_t = const variant&; + + BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), bool >)); + } + + { + using v_t = const variant&; + + BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), bool >)); + } + + { + using v_t = const variant&; + + BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), bool >)); + } + + { + using v_t = const variant&; + + BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), not_found>)); + } + + { + using v_t = const variant&; + + BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); + BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), not_found>)); + } + + return boost::report_errors(); +} From 1515dd5129885d852528980c6866d8c10e4f5cee Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 12:38:45 +0200 Subject: [PATCH 02/10] launched CI From 1b7f95d58eac386fa197ac40bc6fa48047c47ab9 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 12:41:40 +0200 Subject: [PATCH 03/10] set ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0ce631..fac296d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ on: env: UBSAN_OPTIONS: print_stacktrace=1 + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true jobs: posix: From 63dbe5330d243da666e322f27469fa3f19ef75a9 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 13:33:05 +0200 Subject: [PATCH 04/10] used expression SFINAE when available to handle deleted relops --- include/boost/variant2/variant.hpp | 45 +++++++++++++++++++++-------- test/variant_constrained_relops.cpp | 39 ++++++++++++++++++------- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 9f55bd4..79bbbb3 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -2106,16 +2106,42 @@ namespace detail namespace relops_constraints { +#if !defined(BOOST_NO_SFINAE_EXPR) && \ + !BOOST_WORKAROUND(BOOST_MSVC, < 1900) && !BOOST_WORKAROUND(BOOST_GCC, < 40900) + +template struct make_void { typedef void type; }; + +#define BOOST_VARIANT2_DEFINE_RELOP_CONSTRAINT( name, op ) \ +template struct name##_impl: std::false_type {}; \ +\ +template struct name##_impl() op std::declval() )>::type>: \ + std::is_convertible() op std::declval() ), bool> \ +{ \ +}; \ +\ +template struct name: name##_impl {}; + +#else + +// non-expression-SFINAE fallback does not work with deleted relops, +// compilation fails instead, which is good enough + struct not_comparable {}; template not_comparable operator==( U const &, U const & ); template not_comparable operator!=( U const &, U const & ); template not_comparable operator< ( U const &, U const & ); template not_comparable operator<=( U const &, U const & ); -template struct has_eq: std::is_convertible() == std::declval() ), bool> -{ +#define BOOST_VARIANT2_DEFINE_RELOP_CONSTRAINT( name, op ) \ +template struct name: std::is_convertible() op std::declval() ), bool> \ +{ \ }; +#endif + +BOOST_VARIANT2_DEFINE_RELOP_CONSTRAINT( has_eq, == ) + } // namespace relops_constraints template struct eq_L @@ -2143,9 +2169,7 @@ namespace detail namespace relops_constraints { -template struct has_neq: std::is_convertible() != std::declval() ), bool> -{ -}; +BOOST_VARIANT2_DEFINE_RELOP_CONSTRAINT( has_ne, != ) } // namespace relops_constraints @@ -2162,7 +2186,7 @@ template struct ne_L } // namespace detail -template...>::value>::type> +template...>::value>::type> constexpr bool operator!=( variant const & v, variant const & w ) { return v.index() != w.index() || mp11::mp_with_index( v.index(), detail::ne_L{ v, w } ); @@ -2174,9 +2198,7 @@ namespace detail namespace relops_constraints { -template struct has_lt: std::is_convertible() < std::declval() ), bool> -{ -}; +BOOST_VARIANT2_DEFINE_RELOP_CONSTRAINT( has_lt, < ) } // namespace relops_constraints @@ -2211,9 +2233,8 @@ namespace detail namespace relops_constraints { -template struct has_le: std::is_convertible() <= std::declval() ), bool> -{ -}; +BOOST_VARIANT2_DEFINE_RELOP_CONSTRAINT( has_le, <= ) +#undef BOOST_VARIANT2_DEFINE_RELOP_CONSTRAINT } // namespace relops_constraints diff --git a/test/variant_constrained_relops.cpp b/test/variant_constrained_relops.cpp index 1fd9c18..4cf0b8e 100644 --- a/test/variant_constrained_relops.cpp +++ b/test/variant_constrained_relops.cpp @@ -11,6 +11,11 @@ #include #include +#if !defined(BOOST_NO_SFINAE_EXPR) && \ + !BOOST_WORKAROUND(BOOST_MSVC, < 1900) && !BOOST_WORKAROUND(BOOST_GCC, < 40900) +#define USE_DELETED_RELOPS +#endif + using namespace boost::variant2; struct not_found {}; @@ -24,6 +29,9 @@ not_found operator>=( any const &, any const & ); struct no_eq { +#ifdef USE_DELETED_RELOPS + bool operator==( const no_eq& ) const = delete; +#endif int operator!=( const no_eq& ) const; int operator< ( const no_eq& ) const; int operator<=( const no_eq& ) const; @@ -37,25 +45,31 @@ struct bad_eq int operator<=( const bad_eq& ) const; }; -struct no_neq +struct no_ne { - int operator==( const no_neq& ) const; - int operator< ( const no_neq& ) const; - int operator<=( const no_neq& ) const; + int operator==( const no_ne& ) const; +#ifdef USE_DELETED_RELOPS + bool operator!=( const no_ne& ) const = delete; +#endif + int operator< ( const no_ne& ) const; + int operator<=( const no_ne& ) const; }; -struct bad_neq +struct bad_ne { - int operator==( const bad_neq& ) const; - void operator!=( const bad_neq& ) const; - int operator< ( const bad_neq& ) const; - int operator<=( const bad_neq& ) const; + int operator==( const bad_ne& ) const; + void operator!=( const bad_ne& ) const; + int operator< ( const bad_ne& ) const; + int operator<=( const bad_ne& ) const; }; struct no_lt { int operator==( const no_lt& ) const; int operator!=( const no_lt& ) const; +#ifdef USE_DELETED_RELOPS + bool operator< ( const no_lt& ) const = delete; +#endif int operator<=( const no_lt& ) const; }; @@ -72,6 +86,9 @@ struct no_le int operator==( const no_le& ) const; int operator!=( const no_le& ) const; int operator< ( const no_le& ) const; +#ifdef USE_DELETED_RELOPS + bool operator<=( const no_le& ) const = delete; +#endif }; struct bad_le @@ -107,7 +124,7 @@ int main() } { - using v_t = const variant&; + using v_t = const variant&; BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), not_found>)); @@ -118,7 +135,7 @@ int main() } { - using v_t = const variant&; + using v_t = const variant&; BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), not_found>)); From f773a3c34f8506e86a1c718c951df054ca630b8d Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 19:08:32 +0200 Subject: [PATCH 05/10] protected test against C++20 rewritten != --- test/variant_constrained_relops.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/variant_constrained_relops.cpp b/test/variant_constrained_relops.cpp index 4cf0b8e..0ff32ca 100644 --- a/test/variant_constrained_relops.cpp +++ b/test/variant_constrained_relops.cpp @@ -127,7 +127,7 @@ int main() using v_t = const variant&; BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); - BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same(), std::declval() ) ), not_found>)); BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); @@ -138,7 +138,7 @@ int main() using v_t = const variant&; BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); - BOOST_TEST_TRAIT_TRUE((std::is_same() != std::declval() ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same(), std::declval() ) ), not_found>)); BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); From fec619ce00fa54e71d4f48b06b62d987788fcfd9 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 19:22:29 +0200 Subject: [PATCH 06/10] stylistic --- include/boost/variant2/variant.hpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 79bbbb3..cff1c3e 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -2112,20 +2112,18 @@ namespace relops_constraints template struct make_void { typedef void type; }; #define BOOST_VARIANT2_DEFINE_RELOP_CONSTRAINT( name, op ) \ -template struct name##_impl: std::false_type {}; \ +template struct name: std::false_type {}; \ \ -template struct name##_impl struct name() op std::declval() )>::type>: \ std::is_convertible() op std::declval() ), bool> \ { \ -}; \ -\ -template struct name: name##_impl {}; +}; #else -// non-expression-SFINAE fallback does not work with deleted relops, -// compilation fails instead, which is good enough +// non-expression-SFINAE fallback does not work with deleted relops: +// variant's relop instantiation fails instead, which is good enough struct not_comparable {}; template not_comparable operator==( U const &, U const & ); From ea715ab070266dd7f991ed45716b3c0200b39872 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 19:23:00 +0200 Subject: [PATCH 07/10] updated docs --- doc/variant2/reference.adoc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/variant2/reference.adoc b/doc/variant2/reference.adoc index 59b9cb2..0ce2453 100644 --- a/doc/variant2/reference.adoc +++ b/doc/variant2/reference.adoc @@ -845,6 +845,9 @@ template [none] * {blank} + +Constraints: :: `std::declval() == std::declval()` is a valid expression + convertible to `bool` for all `Ti` in `T...`. ++ Returns: :: `v.index() == w.index() && get(v) == get(w)`, where `I` is `v.index()`. @@ -855,6 +858,9 @@ template [none] * {blank} + +Constraints: :: `std::declval() != std::declval()` is a valid expression + convertible to `bool` for all `Ti` in `T...`. ++ Returns: :: `!(v == w)`. ``` @@ -864,6 +870,9 @@ template [none] * {blank} + +Constraints: :: `std::declval() < std::declval()` is a valid expression + convertible to `bool` for all `Ti` in `T...`. ++ Returns: :: `v.index() < w.index() || (v.index() == w.index() && get(v) < get(w))`, where `I` is `v.index()`. @@ -874,6 +883,9 @@ template [none] * {blank} + +Constraints: :: `std::declval() < std::declval()` is a valid expression + convertible to `bool` for all `Ti` in `T...`. ++ Returns: :: `w < v`. ``` @@ -883,6 +895,9 @@ template [none] * {blank} + +Constraints: :: `std::declval() \<= std::declval()` is a valid expression + convertible to `bool` for all `Ti` in `T...`. ++ Returns: :: `v.index() < w.index() || (v.index() == w.index() && get(v) \<= get(w))`, where `I` is `v.index()`. @@ -893,6 +908,9 @@ template [none] * {blank} + +Constraints: :: `std::declval() \<= std::declval()` is a valid expression + convertible to `bool` for all `Ti` in `T...`. ++ Returns: :: `w \<= v`. From 67e664a360e1feafb18eb53b32e0efc937fc185f Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 19:38:44 +0200 Subject: [PATCH 08/10] added test case from https://github.com/boostorg/variant2/issues/60 --- test/variant_constrained_relops.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/variant_constrained_relops.cpp b/test/variant_constrained_relops.cpp index 0ff32ca..a2d6c49 100644 --- a/test/variant_constrained_relops.cpp +++ b/test/variant_constrained_relops.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #if !defined(BOOST_NO_SFINAE_EXPR) && \ @@ -99,6 +100,15 @@ struct bad_le void operator<=( const bad_le& ) const; }; +struct non_comparable {}; + +template +struct wrapper +{ + T t; + bool operator<(const wrapper& x) const { return t < x.t; }; +}; + int main() { { @@ -189,5 +199,15 @@ int main() BOOST_TEST_TRAIT_TRUE((std::is_same() >= std::declval() ), not_found>)); } + { + //https://github.com/boostorg/variant2/issues/60 + + using v_t = variant; + using reference_wrapper = std::reference_wrapper; + + wrapper x{nullptr}, y{nullptr}; + (void)(x < y); + } + return boost::report_errors(); } From db3698fe1417a9c3f8e8361a38a2b75baebe968c Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 17 Jul 2026 19:48:52 +0200 Subject: [PATCH 09/10] stylistic --- test/variant_constrained_relops.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/variant_constrained_relops.cpp b/test/variant_constrained_relops.cpp index a2d6c49..e026421 100644 --- a/test/variant_constrained_relops.cpp +++ b/test/variant_constrained_relops.cpp @@ -148,7 +148,7 @@ int main() using v_t = const variant&; BOOST_TEST_TRAIT_TRUE((std::is_same() == std::declval() ), bool >)); - BOOST_TEST_TRAIT_TRUE((std::is_same(), std::declval() ) ), not_found>)); + BOOST_TEST_TRAIT_TRUE((std::is_same(), std::declval() ) ), not_found>)); BOOST_TEST_TRAIT_TRUE((std::is_same() < std::declval() ), bool >)); BOOST_TEST_TRAIT_TRUE((std::is_same() <= std::declval() ), bool >)); BOOST_TEST_TRAIT_TRUE((std::is_same() > std::declval() ), bool >)); From 5bc84a2ab34bb80a8de12629223fca593c81a65c Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 18 Jul 2026 12:24:29 +0200 Subject: [PATCH 10/10] replaced any in variant_constrained_relops.cpp with a variant-specific sink --- test/variant_constrained_relops.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/variant_constrained_relops.cpp b/test/variant_constrained_relops.cpp index e026421..ed3ad40 100644 --- a/test/variant_constrained_relops.cpp +++ b/test/variant_constrained_relops.cpp @@ -20,13 +20,13 @@ using namespace boost::variant2; struct not_found {}; -struct any { any( ... ); }; -not_found operator==( any const &, any const & ); -not_found operator!=( any const &, any const & ); -not_found operator< ( any const &, any const & ); -not_found operator<=( any const &, any const & ); -not_found operator> ( any const &, any const & ); -not_found operator>=( any const &, any const & ); +struct variant_sink { template variant_sink( variant const & ); }; +not_found operator==( variant_sink const &, variant_sink const & ); +not_found operator!=( variant_sink const &, variant_sink const & ); +not_found operator< ( variant_sink const &, variant_sink const & ); +not_found operator<=( variant_sink const &, variant_sink const & ); +not_found operator> ( variant_sink const &, variant_sink const & ); +not_found operator>=( variant_sink const &, variant_sink const & ); struct no_eq {