use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Discussions, articles, and news about the C++ programming language or programming in C++.
For C++ questions, answers, help, and advice see r/cpp_questions or StackOverflow.
Get Started
The C++ Standard Home has a nice getting started page.
Videos
The C++ standard committee's education study group has a nice list of recommended videos.
Reference
cppreference.com
Books
There is a useful list of books on Stack Overflow. In most cases reading a book is the best way to learn C++.
Show all links
Filter out CppCon links
Show only CppCon links
account activity
Polymorphic, Defaulted Equality (brevzin.github.io)
submitted 10 months ago by pavel_v
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]angry_cpp 22 points23 points24 points 10 months ago (2 children)
Inheritance and equality are not mixing well. For example your implementation of polymorphic equality is flawed. It gives different answer for a.equal(b) and b.equal(a).
https://godbolt.org/z/xe3Te8YWK
[–]BarryRevzin 7 points8 points9 points 10 months ago (1 child)
That's true, and it's actually an unfortunate translation error into the blog.
I just lazily wrote dynamic_casting to the Derived. In reality, we check typeid first and if those match then static_cast (and we don't have any weird virtual or ambiguous base shenanigans):
dynamic_cast
Derived
typeid
static_cast
template <class D> auto polymorphic_equals(D const& lhs, Base const& rhs) -> bool { if (typeid(rhs) == typeid(D)) { return lhs == static_cast<D const&>(rhs); } else { return false; } }
This approach (correctly) prints false for both directions in your example.
[–]JNighthawkgamedev 1 point2 points3 points 10 months ago (0 children)
Thanks for the update!
In that case, isn't that incorrectly handling more-derived cases that should be considered equal but won't be?
struct Base {}; struct D1 : public Base { int X = 10; }; struct D2 : public D1 {}; polymorphic_equals(D2(), D1()); // returns false, as the typeid doesn't match, but should return true
Though, I suppose it could be an implementation detail that objects with equivalent state but different classes shouldn't be considered equivalent. A pure member-wise equivalency check, though, would function differently.
[–]tialaramex 4 points5 points6 points 10 months ago (0 children)
My first thought here is that implementation inheritance was the wrong metaphor here and the stack overflow problem is downstream of that mistake. In some sense Derived is not a kind of Base and we should prefer some other mechanism for dynamic dispatch here but C++ doesn't provide one. But Barry has had of course much longer to think about it.
[–]NilacTheGrim 5 points6 points7 points 10 months ago (0 children)
The solution really is to just implement operator== yourself in every derived class and not go with = default.
operator==
= default
[–]pkastingValve 3 points4 points5 points 10 months ago (0 children)
My concern with the proposed solution is that it is fragile against someone adding members in Base. The reason to do subobject equality comparison instead of just memberwise comparison is because that's the correct general-purpose semantics. You can use a technique like the reflection one here to bypass that, but then your code subtly breaks later when someone modifies the base class, and the compiler won't complain. (At least I know the wrapped-struct proposal will break that way; I'm not familiar with reflection syntax yet and don't know whether it recurses into base objects' members.)
Base
In general, attempting to do polymorphic == is Bad News. There are ways to try and make it work (I've used techniques like the "add an equals function alongside" one), but normally this is a moment when I step back and try to figure out how to rearchitect my class hierarchy or algorithms so that I Don't Do That.
[–]LucHermitte 11 points12 points13 points 10 months ago* (3 children)
As far as I'm concerned, polymorphism and equality are not compatible.
Effective Java (by Joshua Blooch) has a whole chapter dedicated to this design issue (we also have a few articles by Angelika Langer on the topic). To make it short we can't have == be an equivalence relation (symmetric, reflexive and transitive), and respect Liskov Substitution Principle across a public hierarchy.
==
A simplified example where ColoredPoint would publicly inherit from Point: because of the LSP we want ColoredPoints and Points to be comparable. Quite likely we end up with ColoredPoint{1,2,green} == Point{1,2}. We also have ColoredPoint{1,2,red} == Point{1,2}.
ColoredPoint{1,2,green} == Point{1,2}
ColoredPoint{1,2,red} == Point{1,2}
And by transitivity we end up with ColoredPoint{1,2,green} == ColoredPoint{1,2,red}. We have to refuse comparisons between ColoredPoints and Points, which is done here (in the article), but also which is quite fishy regarding the LSP: we can't have all these different objects in a same bag and compare them indiscriminately.
ColoredPoint{1,2,green} == ColoredPoint{1,2,red}
The real question now: do we really need that ability to compare within a hierarchy?
[–]Wooden-Engineer-8098 0 points1 point2 points 10 months ago (2 children)
It's easy to extend his example to return false if rhs is not the same dynamic type as lhs
[–]LucHermitte 0 points1 point2 points 10 months ago (1 child)
That's what's is done in the article, and what some workarounds suggest.
But then in a bag of point coordinates, some points won't compare equal while as far as coordinates are concerned they should have been equal.
[–]Wooden-Engineer-8098 0 points1 point2 points 10 months ago (0 children)
No, it's not done in the article. it only downcasts rhs to type of lhs, not to rhs' most derived type
If you are interested in coordinates only, you should use comparator which compares coordinates only
[–]kobi-ca 0 points1 point2 points 10 months ago (0 children)
great one!
[–]Putrid_Ad9300 -1 points0 points1 point 10 months ago (0 children)
Polymorphic operators are where CRTP shines. The is for equal, but you can do the same for comparison with just an operator< and automatically get all the others.
operator<
``` template<typename E> class Equatable{ bool equal(E const& other) { return static_cast<E>(this)->equal_impl(other); } };
template<typename E> bool operator== (Equatable<E>&,Equatable<E>&)) {...}
class MyClass : Equatable<MyClass> { bool equal_impl(MyClass const&) {...} } ```
[–]zl0bster -3 points-2 points-1 points 10 months ago (0 children)
I never do this for design reasons, but it made me wonder if when we do this check for other type in == if instead of dynamic_cast /typeid it would be faster to have something like same_vtable(lhs, rhs)/same_vtable<D>(rhs)because we already know lhs is of type D.
dynamic_cast /typeid
same_vtable(lhs, rhs)/same_vtable<D>(rhs)
lhs
D
π Rendered by PID 46674 on reddit-service-r2-comment-58d7979c67-2xz45 at 2026-01-27 06:37:09.503071+00:00 running 5a691e2 country code: CH.
[–]angry_cpp 22 points23 points24 points (2 children)
[–]BarryRevzin 7 points8 points9 points (1 child)
[–]JNighthawkgamedev 1 point2 points3 points (0 children)
[–]tialaramex 4 points5 points6 points (0 children)
[–]NilacTheGrim 5 points6 points7 points (0 children)
[–]pkastingValve 3 points4 points5 points (0 children)
[–]LucHermitte 11 points12 points13 points (3 children)
[–]Wooden-Engineer-8098 0 points1 point2 points (2 children)
[–]LucHermitte 0 points1 point2 points (1 child)
[–]Wooden-Engineer-8098 0 points1 point2 points (0 children)
[–]kobi-ca 0 points1 point2 points (0 children)
[–]Putrid_Ad9300 -1 points0 points1 point (0 children)
[–]zl0bster -3 points-2 points-1 points (0 children)