3030#include < utility>
3131#endif
3232
33+ // GCC < 10 workaround for std::optional comparison ambiguity (see below)
34+ #if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \
35+ (__cplusplus >= 201703L || (defined (_MSVC_LANG) && _MSVC_LANG >= 201703L ))
36+ #include < optional>
37+ #endif
38+
3339#include < boost/test/detail/suppress_warnings.hpp>
3440
3541// ____________________________________________________________________________//
@@ -38,6 +44,28 @@ namespace boost {
3844namespace test_tools {
3945namespace assertion {
4046
47+ // ************************************************************************** //
48+ // ************** assertion::is_std_optional ************** //
49+ // ************************************************************************** //
50+ // Trait to detect std::optional. Used for GCC < 10 workaround.
51+
52+ template <typename T>
53+ struct is_std_optional : std::false_type {};
54+
55+ #if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \
56+ (__cplusplus >= 201703L || (defined (_MSVC_LANG) && _MSVC_LANG >= 201703L ))
57+
58+ template <typename T>
59+ struct is_std_optional <std::optional<T>> : std::true_type {};
60+ template <typename T>
61+ struct is_std_optional <std::optional<T>&> : std::true_type {};
62+ template <typename T>
63+ struct is_std_optional <std::optional<T> const &> : std::true_type {};
64+ template <typename T>
65+ struct is_std_optional <std::optional<T>&&> : std::true_type {};
66+
67+ #endif
68+
4169// ************************************************************************** //
4270// ************** assertion::operators ************** //
4371// ************************************************************************** //
@@ -77,7 +105,8 @@ namespace op {
77105
78106#ifndef BOOST_NO_CXX11_DECLTYPE
79107
80- #define BOOST_TEST_FOR_EACH_CONST_OP (action )\
108+ // Non-comparison operators (never need SFINAE workaround)
109+ #define BOOST_TEST_FOR_EACH_NONCOMP_OP (action )\
81110 action (->*, MEMP, ->*, MEMP ) \
82111 \
83112 action( * , MUL , * , MUL ) \
@@ -90,15 +119,20 @@ namespace op {
90119 action( <<, LSH , << , LSH ) \
91120 action( >>, RSH , >> , RSH ) \
92121 \
93- BOOST_TEST_FOR_EACH_COMP_OP(action) \
94- \
95122 action( & , BAND, & , BAND ) \
96123 action( ^ , XOR , ^ , XOR ) \
97124 action( | , BOR , | , BOR ) \
98125/* */
99126
127+ #define BOOST_TEST_FOR_EACH_CONST_OP (action )\
128+ BOOST_TEST_FOR_EACH_NONCOMP_OP (action) \
129+ BOOST_TEST_FOR_EACH_COMP_OP(action) \
130+ /* */
131+
100132#else
101133
134+ #define BOOST_TEST_FOR_EACH_NONCOMP_OP (action )
135+
102136#define BOOST_TEST_FOR_EACH_CONST_OP (action )\
103137 BOOST_TEST_FOR_EACH_COMP_OP (action) \
104138/* */
@@ -181,20 +215,87 @@ BOOST_TEST_FOR_EACH_CONST_OP( DEFINE_CONST_OPER )
181215} // namespace op
182216
183217// ************************************************************************** //
184- // ************** assertion::expression_base ************** //
218+ // ************** assertion::optional_friends_base ************** //
185219// ************************************************************************** //
186- // Defines expression operators
220+ // GCC < 10 workaround: Base class that conditionally provides hidden friend
221+ // comparison operators only when ValType is std::optional. Hidden friends are
222+ // found via ADL and as non-templates beat std::optional's template operators.
223+ // See: https://github.com/boostorg/test/issues/475
187224
188225template <typename Lhs, typename Rhs, typename OP> class binary_expr ;
189226
227+ #if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \
228+ (__cplusplus >= 201703L || (defined (_MSVC_LANG) && _MSVC_LANG >= 201703L ))
229+
230+ // Primary template - empty (for non-optional types)
231+ template <typename ExprType, typename ValType, bool IsOptional = is_std_optional<ValType>::value>
232+ struct optional_friends_base {};
233+
234+ // Specialization for std::optional types - provides hidden friend operators
235+ template <typename ExprType, typename ValType>
236+ struct optional_friends_base <ExprType, ValType, true > {
237+ friend binary_expr<ExprType, ValType, op::EQ<ValType, ValType>>
238+ operator ==( ExprType lhs, ValType rhs )
239+ {
240+ return binary_expr<ExprType, ValType, op::EQ<ValType, ValType>>(
241+ std::move (lhs), std::move (rhs));
242+ }
243+
244+ friend binary_expr<ExprType, ValType, op::NE<ValType, ValType>>
245+ operator !=( ExprType lhs, ValType rhs )
246+ {
247+ return binary_expr<ExprType, ValType, op::NE<ValType, ValType>>(
248+ std::move (lhs), std::move (rhs));
249+ }
250+
251+ friend binary_expr<ExprType, ValType, op::LT<ValType, ValType>>
252+ operator <( ExprType lhs, ValType rhs )
253+ {
254+ return binary_expr<ExprType, ValType, op::LT<ValType, ValType>>(
255+ std::move (lhs), std::move (rhs));
256+ }
257+
258+ friend binary_expr<ExprType, ValType, op::LE<ValType, ValType>>
259+ operator <=( ExprType lhs, ValType rhs )
260+ {
261+ return binary_expr<ExprType, ValType, op::LE<ValType, ValType>>(
262+ std::move (lhs), std::move (rhs));
263+ }
264+
265+ friend binary_expr<ExprType, ValType, op::GT<ValType, ValType>>
266+ operator >( ExprType lhs, ValType rhs )
267+ {
268+ return binary_expr<ExprType, ValType, op::GT<ValType, ValType>>(
269+ std::move (lhs), std::move (rhs));
270+ }
271+
272+ friend binary_expr<ExprType, ValType, op::GE<ValType, ValType>>
273+ operator >=( ExprType lhs, ValType rhs )
274+ {
275+ return binary_expr<ExprType, ValType, op::GE<ValType, ValType>>(
276+ std::move (lhs), std::move (rhs));
277+ }
278+ };
279+
280+ #else // Not GCC < 10
281+ template <typename ExprType, typename ValType, bool IsOptional = false >
282+ struct optional_friends_base {};
283+ #endif // GCC < 10 && C++17
284+
285+ // ************************************************************************** //
286+ // ************** assertion::expression_base ************** //
287+ // ************************************************************************** //
288+ // Defines expression operators
289+
190290template <typename ExprType,typename ValType>
191- class expression_base {
291+ class expression_base : public optional_friends_base <ExprType, ValType> {
192292public:
193293
194294#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
195295 template <typename T>
196296 struct RhsT : remove_const<typename remove_reference<T>::type> {};
197-
297+
298+ // Regular operator support (non-comparison operators)
198299#define ADD_OP_SUPPORT ( oper, name, _, _i ) \
199300 template <typename T> \
200301 binary_expr<ExprType,T, \
@@ -208,7 +309,33 @@ class expression_base {
208309 std::forward<T>(rhs) ); \
209310 } \
210311/* */
312+
313+ // GCC < 10 workaround: comparison operators with SFINAE to exclude std::optional
314+ // when ValType is also std::optional. The hidden friend operators in the base class
315+ // optional_friends_base will handle the optional==optional case instead.
316+ #if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \
317+ (__cplusplus >= 201703L || (defined (_MSVC_LANG) && _MSVC_LANG >= 201703L ))
318+ #define ADD_OP_SUPPORT_COMP ( oper, name, _, _i ) \
319+ template <typename T, \
320+ typename std::enable_if< \
321+ !is_std_optional<ValType>::value || \
322+ !is_std_optional<typename RhsT<T>::type>::value, int >::type = 0 > \
323+ binary_expr<ExprType,T, \
324+ op::name<ValType,typename RhsT<T>::type> > \
325+ operator oper ( T&& rhs ) \
326+ { \
327+ return binary_expr<ExprType,T, \
328+ op::name<ValType,typename RhsT<T>::type> > \
329+ ( std::forward<ExprType>( \
330+ *static_cast <ExprType*>(this ) ), \
331+ std::forward<T>(rhs) ); \
332+ } \
333+ /* */
211334#else
335+ #define ADD_OP_SUPPORT_COMP ADD_OP_SUPPORT
336+ #endif
337+
338+ #else // BOOST_NO_CXX11_RVALUE_REFERENCES
212339
213340#define ADD_OP_SUPPORT ( oper, name, _, _i ) \
214341 template <typename T> \
@@ -222,10 +349,14 @@ class expression_base {
222349 rhs ); \
223350 } \
224351/* */
225- #endif
352+ #define ADD_OP_SUPPORT_COMP ADD_OP_SUPPORT
353+
354+ #endif // BOOST_NO_CXX11_RVALUE_REFERENCES
226355
227- BOOST_TEST_FOR_EACH_CONST_OP ( ADD_OP_SUPPORT )
356+ BOOST_TEST_FOR_EACH_NONCOMP_OP ( ADD_OP_SUPPORT )
357+ BOOST_TEST_FOR_EACH_COMP_OP ( ADD_OP_SUPPORT_COMP )
228358 #undef ADD_OP_SUPPORT
359+ #undef ADD_OP_SUPPORT_COMP
229360
230361#ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS
231362 // Disabled operators
0 commit comments