Skip to content

Commit 4f5157b

Browse files
ricejasonfldionne
authored andcommitted
Fixes operator== with std::optional
- Prevents comparable operators from considering the tag types like tuple_tag - Allows Struct classes to implement their own comparable operators so it can be used with types like std::optional
1 parent 381c76f commit 4f5157b

File tree

5 files changed

+92
-5
lines changed

5 files changed

+92
-5
lines changed

include/boost/hana/core/tag_of.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Distributed under the Boost Software License, Version 1.0.
1515
#include <boost/hana/config.hpp>
1616
#include <boost/hana/core/when.hpp>
1717

18+
#include <type_traits>
19+
1820

1921
BOOST_HANA_NAMESPACE_BEGIN
2022
//! @cond
@@ -44,6 +46,14 @@ BOOST_HANA_NAMESPACE_BEGIN
4446
template <typename T> struct tag_of<T const volatile> : tag_of<T> { };
4547
template <typename T> struct tag_of<T&> : tag_of<T> { };
4648
template <typename T> struct tag_of<T&&> : tag_of<T> { };
49+
50+
namespace detail {
51+
template <typename T>
52+
struct has_idempotent_tag
53+
: std::is_same<hana::tag_of_t<T>,
54+
std::remove_const_t<std::remove_reference_t<T>>>
55+
{ };
56+
}
4757
BOOST_HANA_NAMESPACE_END
4858

4959
#endif // !BOOST_HANA_CORE_TAG_OF_HPP

include/boost/hana/detail/operators/comparable.hpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,23 @@ BOOST_HANA_NAMESPACE_BEGIN namespace detail {
2626

2727
namespace operators {
2828
template <typename X, typename Y, typename = typename std::enable_if<
29-
detail::comparable_operators<typename hana::tag_of<X>::type>::value ||
30-
detail::comparable_operators<typename hana::tag_of<Y>::type>::value
29+
!detail::has_idempotent_tag<X>::value &&
30+
!detail::has_idempotent_tag<Y>::value &&
31+
(detail::comparable_operators<
32+
typename hana::tag_of<X>::type>::value ||
33+
detail::comparable_operators<
34+
typename hana::tag_of<Y>::type>::value)
3135
>::type>
3236
constexpr auto operator==(X&& x, Y&& y)
3337
{ return hana::equal(static_cast<X&&>(x), static_cast<Y&&>(y)); }
3438

3539
template <typename X, typename Y, typename = typename std::enable_if<
36-
detail::comparable_operators<typename hana::tag_of<X>::type>::value ||
37-
detail::comparable_operators<typename hana::tag_of<Y>::type>::value
40+
!detail::has_idempotent_tag<X>::value &&
41+
!detail::has_idempotent_tag<Y>::value &&
42+
(detail::comparable_operators<
43+
typename hana::tag_of<X>::type>::value ||
44+
detail::comparable_operators<
45+
typename hana::tag_of<Y>::type>::value)
3846
>::type>
3947
constexpr auto operator!=(X&& x, Y&& y)
4048
{ return hana::not_equal(static_cast<X&&>(x), static_cast<Y&&>(y)); }

include/boost/hana/equal.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,10 @@ BOOST_HANA_NAMESPACE_BEGIN
191191
}
192192

193193
template <typename S>
194-
struct equal_impl<S, S, when<hana::Struct<S>::value>> {
194+
struct equal_impl<S, S, when<
195+
hana::Struct<S>::value &&
196+
!detail::EqualityComparable<S, S>::value
197+
>> {
195198
template <typename X, typename Y>
196199
static constexpr auto apply(X const& x, Y const& y) {
197200
return hana::all_of(hana::accessors<S>(),

test/issues/github_460.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright Jason Rice 2020
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
4+
5+
#include <boost/hana/define_struct.hpp>
6+
#include <boost/hana/equal.hpp>
7+
#include <boost/hana/members.hpp>
8+
#include <boost/hana/not_equal.hpp>
9+
namespace hana = boost::hana;
10+
11+
12+
struct SomeStruct {
13+
BOOST_HANA_DEFINE_STRUCT(SomeStruct, (int, x));
14+
15+
constexpr bool operator==(SomeStruct const& other) {
16+
return hana::equal(hana::members(*this), hana::members(other));
17+
}
18+
19+
constexpr bool operator!=(SomeStruct const& other) {
20+
return hana::not_equal(hana::members(*this), hana::members(other));
21+
}
22+
};
23+
24+
int main() {
25+
static_assert(SomeStruct{5} == SomeStruct{5}, "");
26+
static_assert(hana::equal(SomeStruct{5}, SomeStruct{5}), "");
27+
}

test/issues/github_470.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright Jason Rice 2020
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
4+
5+
#include <boost/hana/equal.hpp>
6+
#include <boost/hana/not_equal.hpp>
7+
#include <boost/hana/tuple.hpp>
8+
#include <cassert>
9+
namespace hana = boost::hana;
10+
11+
namespace {
12+
template <typename T>
13+
struct optional {
14+
T t;
15+
};
16+
17+
template <typename T>
18+
constexpr bool operator==(optional<T> const& o, T const& t) {
19+
return o.t == t;
20+
}
21+
template <typename T>
22+
constexpr bool operator==(T const& t, optional<T> const& o) {
23+
return o.t == t;
24+
}
25+
template <typename T>
26+
constexpr bool operator!=(optional<T> const& o, T const& t) {
27+
return o.t != t;
28+
}
29+
template <typename T>
30+
constexpr bool operator!=(T const& t, optional<T> const& o) {
31+
return o.t != t;
32+
}
33+
}
34+
35+
int main() {
36+
boost::hana::tuple<int> x{};
37+
optional<boost::hana::tuple<int>> attr{x};
38+
assert(attr == x); // <-- Kablooey!
39+
}

0 commit comments

Comments
 (0)