You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
compare_pointees.hpp should permit a predicate #72
and I want to test equality (or less-than-ness) of the float part, still treating them as optional. That is, thinking of them as optional floats. If equal_pointees had a predicate argument, I could do
The particular case I'd use this is where I've got a big struct representing the state of a view. For the current frame, I have a pointer to the previous one and a pointer to the current one and I want to decide if I have to a certain part of the work of redrawing. That is, either they have different nullness that's different, or if they are both non-null then compare a projection of them that pertains to the thing I care about.
I thought more about this. I think(?) this is the basis function we'd really want:
template <typename OptA, typename OptB,
typename Cmp = std::compare_three_way,
typename Proj = std::identity>
[[nodiscard]]
auto pointees_compare(const OptA& a, const OptB& b,
Cmp cmp = {},
Proj proj = {})
-> decltype(cmp(std::invoke(proj, *a), std::invoke(proj, *b)))
{
if (a and b) {
return cmp(std::invoke(proj, *a), std::invoke(proj, *b));
}
return cmp(static_cast<bool>(a), static_cast<bool>(b));
}
Compared with the existing equal_pointees, it...
Allows you to compare a std::optional<int> with a const float* (the pointer-like types can be different). This also means if you have a std::optional<BigClass> a and a BigClass b, you can call pointees_compare(a, &b).
Allows you to project the dereferenced objects, so you can conceptually compare a->x with b->x by using [](auto& a) { return a.x; } as the projection function.
By using std::invoke, it lets you pass &Class::memfun as the projection.
It supports equality, less-than, three-way, and others.
The only other thing I could see adding here is having the nullness comparison use a special type, so the caller could overload their cmp to do something special, like compare two int*s where nullptr compares "more" than not-null, but the ints compare in numerical order. (That is, struct truthiness_t { bool truthy; explicit truthiness_t(auto& x) : truthy(static_cast<bool>(x)) {} }; and then return cmp(truthiness_t(a), truthiness_t(b); But that seems like overkill. https://godbolt.org/z/ejKW7dd55
We could also allow for special null types, like std::nullptr_t and std::nullopt_t that can't be dereferenced, but that seems like a lot of extra work, and inelegant.
It's a basis in that the existing equal_pointees can be implemented in terms of it as
template<class OptionalPointee>
inline
bool equal_pointees ( OptionalPointee const& x, OptionalPointee const& y )
{
return pointees_compare(x, y, std::equal_to{});
}
and less_pointees:
template<class OptionalPointee>
inline
bool less_pointees ( OptionalPointee const& x, OptionalPointee const& y )
{
return pointees_compare(x, y, std::less{});
}
I keep coming back to this because I have an old function that has a wall of if (needToUpdateX(oldStatePtr, newStatePtr)) {...} where needToUpdateX is true if the pointers have different nullness or if both are valid but oldStatePtr->X() != newStatePtr->X(), so all of these needToUpdateX functions could be replaced by pointes_compare(oldStatePtr, newStatePtr, std::not_equal_to{}, &MyClass::X).
If we move toward the pointees_compare approach suggested by Ben, we should ensure the implementation correctly handles the short-circuiting logic for the three-way comparison, specifically when one optional is empty and the other is not. Rather than just taking a projection, would it be cleaner to pass a binary predicate to equal_pointees as originally requested, and keep pointees_compare strictly for three-way results? I am concerned that overloading pointees_compare to handle both equality and ordering might lead to ambiguous template resolution if the user provides a custom binary predicate that doesn't map neatly to std::compare_three_way.
Activity
BenFrantzDale commentedon Feb 16, 2021
For that matter, I should be able compare pointees of different type, provided
operator==applies to both dereferenced. That is, I think it should beBenFrantzDale commentedon Oct 7, 2022
I thought more about this. I think(?) this is the basis function we'd really want:
Compared with the existing
equal_pointees, it...std::optional<int>with aconst float*(the pointer-like types can be different). This also means if you have astd::optional<BigClass> aand aBigClass b, you can callpointees_compare(a, &b).a->xwithb->xby using[](auto& a) { return a.x; }as the projection function.std::invoke, it lets you pass&Class::memfunas the projection.cmpto do something special, like compare twoint*s wherenullptrcompares "more" than not-null, but the ints compare in numerical order. (That is,struct truthiness_t { bool truthy; explicit truthiness_t(auto& x) : truthy(static_cast<bool>(x)) {} };and thenreturn cmp(truthiness_t(a), truthiness_t(b);But that seems like overkill. https://godbolt.org/z/ejKW7dd55std::nullptr_tandstd::nullopt_tthat can't be dereferenced, but that seems like a lot of extra work, and inelegant.It's a basis in that the existing
equal_pointeescan be implemented in terms of it asand
less_pointees:I keep coming back to this because I have an old function that has a wall of
if (needToUpdateX(oldStatePtr, newStatePtr)) {...}whereneedToUpdateXistrueif the pointers have different nullness or if both are valid butoldStatePtr->X() != newStatePtr->X(), so all of theseneedToUpdateXfunctions could be replaced bypointes_compare(oldStatePtr, newStatePtr, std::not_equal_to{}, &MyClass::X).https://godbolt.org/z/6EEb4GKc9
eliottramanda26 commentedon Oct 7, 2026
If we move toward the
pointees_compareapproach suggested by Ben, we should ensure the implementation correctly handles the short-circuiting logic for the three-way comparison, specifically when one optional is empty and the other is not. Rather than just taking a projection, would it be cleaner to pass a binary predicate toequal_pointeesas originally requested, and keeppointees_comparestrictly for three-way results? I am concerned that overloadingpointees_compareto handle both equality and ordering might lead to ambiguous template resolution if the user provides a custom binary predicate that doesn't map neatly tostd::compare_three_way.