@@ -13,6 +13,7 @@
1313#include <catch2/internal/catch_compare_traits.hpp>
1414#include <catch2/internal/catch_test_failure_exception.hpp>
1515#include <catch2/internal/catch_logical_traits.hpp>
16+#include <catch2/internal/catch_compiler_capabilities.hpp>
16171718#include <type_traits>
1819#include <iosfwd>
@@ -34,8 +35,33 @@
3435# pragma GCC diagnostic ignored "-Wsign-compare"
3536#endif
363738+#if defined(CATCH_CPP20_OR_GREATER) && __has_include(<compare>)
39+# include <compare>
40+# if defined( __cpp_lib_three_way_comparison ) && \
41+ __cpp_lib_three_way_comparison >= 201907L
42+# define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS
43+# endif
44+#endif
45+3746namespace Catch {
384748+// Note: There is nothing that stops us from extending this,
49+// e.g. to `std::is_scalar`, but the more encompassing
50+// traits are usually also more expensive. For now we
51+// keep this as it used to be and it can be changed later.
52+template <typename T>
53+struct capture_by_value
54+ : std::integral_constant<bool, std::is_arithmetic<T>{}> {};
55+56+#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS )
57+template <>
58+struct capture_by_value<std::strong_ordering> : std::true_type {};
59+template <>
60+struct capture_by_value<std::weak_ordering> : std::true_type {};
61+template <>
62+struct capture_by_value<std::partial_ordering> : std::true_type {};
63+#endif
64+3965template <typename T>
4066struct always_false : std::false_type {};
4167@@ -44,11 +70,12 @@ namespace Catch {
4470bool m_result;
45714672public:
47-auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
48-auto getResult() const -> bool { return m_result; }
49-virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
73+constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
74+constexpr auto getResult() const -> bool { return m_result; }
75+//! This function **has** to be overriden by the derived class.
76+virtual void streamReconstructedExpression( std::ostream& os ) const;
507751-ITransientExpression( bool isBinaryExpression, bool result )
78+constexpr ITransientExpression( bool isBinaryExpression, bool result )
5279 : m_isBinaryExpression( isBinaryExpression ),
5380 m_result( result )
5481 {}
@@ -59,7 +86,7 @@ namespace Catch {
59866087// We don't actually need a virtual destructor, but many static analysers
6188// complain if it's not here :-(
62-virtual ~ITransientExpression(); // = default;
89+virtual ~ITransientExpression() = default;
63906491friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) {
6592 expr.streamReconstructedExpression(out);
@@ -81,7 +108,7 @@ namespace Catch {
81108 }
8210983110public:
84-BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
111+constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
85112 : ITransientExpression{ true, comparisonResult },
86113m_lhs( lhs ),
87114m_op( op ),
@@ -154,7 +181,7 @@ namespace Catch {
154181 }
155182156183public:
157-explicit UnaryExpr( LhsT lhs )
184+explicit constexpr UnaryExpr( LhsT lhs )
158185 : ITransientExpression{ false, static_cast<bool>(lhs) },
159186m_lhs( lhs )
160187 {}
@@ -165,30 +192,30 @@ namespace Catch {
165192class ExprLhs {
166193 LhsT m_lhs;
167194public:
168-explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
195+explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
169196170197#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \
171198template <typename RhsT> \
172-friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
199+constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
173200 ->std::enable_if_t< \
174201 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
175- Detail::negation<std::is_arithmetic< \
202+ Detail::negation<capture_by_value< \
176203 std::remove_reference_t<RhsT>>>>::value, \
177204 BinaryExpr<LhsT, RhsT const&>> { \
178205return { \
179206static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
180207 } \
181208template <typename RhsT> \
182-friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
209+constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
183210 ->std::enable_if_t< \
184211 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
185-std::is_arithmetic<RhsT>>::value, \
212+capture_by_value<RhsT>>::value, \
186213 BinaryExpr<LhsT, RhsT>> { \
187214return { \
188215static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
189216 } \
190217template <typename RhsT> \
191-friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
218+constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
192219 ->std::enable_if_t< \
193220 Detail::conjunction< \
194221 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
@@ -202,7 +229,7 @@ namespace Catch {
202229static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
203230 } \
204231template <typename RhsT> \
205-friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
232+constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
206233 ->std::enable_if_t< \
207234 Detail::conjunction< \
208235 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
@@ -220,28 +247,29 @@ namespace Catch {
220247221248 #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR
222249250+223251#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \
224252template <typename RhsT> \
225-friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
253+constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
226254 ->std::enable_if_t< \
227255 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
228- Detail::negation<std::is_arithmetic< \
256+ Detail::negation<capture_by_value< \
229257 std::remove_reference_t<RhsT>>>>::value, \
230258 BinaryExpr<LhsT, RhsT const&>> { \
231259return { \
232260static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
233261 } \
234262template <typename RhsT> \
235-friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
263+constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
236264 ->std::enable_if_t< \
237265 Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
238-std::is_arithmetic<RhsT>>::value, \
266+capture_by_value<RhsT>>::value, \
239267 BinaryExpr<LhsT, RhsT>> { \
240268return { \
241269static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
242270 } \
243271template <typename RhsT> \
244-friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
272+constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
245273 ->std::enable_if_t< \
246274 Detail::conjunction< \
247275 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
@@ -253,7 +281,7 @@ namespace Catch {
253281static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
254282 } \
255283template <typename RhsT> \
256-friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
284+constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
257285 ->std::enable_if_t< \
258286 Detail::conjunction< \
259287 Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
@@ -274,16 +302,16 @@ namespace Catch {
274302275303#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \
276304template <typename RhsT> \
277-friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
305+constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
278306 ->std::enable_if_t< \
279- !std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
307+ !capture_by_value<std::remove_reference_t<RhsT>>::value, \
280308 BinaryExpr<LhsT, RhsT const&>> { \
281309return { \
282310static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
283311 } \
284312template <typename RhsT> \
285-friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
286- ->std::enable_if_t<std::is_arithmetic<RhsT>::value, \
313+constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
314+ ->std::enable_if_t<capture_by_value<RhsT>::value, \
287315 BinaryExpr<LhsT, RhsT>> { \
288316return { \
289317static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
@@ -309,19 +337,23 @@ namespace Catch {
309337"wrap the expression inside parentheses, or decompose it");
310338 }
311339312-auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
340+constexpr auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
313341return UnaryExpr<LhsT>{ m_lhs };
314342 }
315343 };
316344317345struct Decomposer {
318-template<typename T, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<T>>::value, int> = 0>
319-friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {
346+template <typename T,
347+ std::enable_if_t<
348+ !capture_by_value<std::remove_reference_t<T>>::value,
349+int> = 0>
350+constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {
320351return ExprLhs<const T&>{ lhs };
321352 }
322353323-template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
324-friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {
354+template <typename T,
355+ std::enable_if_t<capture_by_value<T>::value, int> = 0>
356+constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {
325357return ExprLhs<T>{ value };
326358 }
327359 };