I have the following code. There is a base class (Node) and a derived class (NumberExprNode) that each have a virtual equality method.
#include <vector>
#include <memory>
#include <iostream>
template<typename T>
bool operator==(const std::vector<std::unique_ptr<T>>& n1, const std::vector<std::unique_ptr<T>>& n2) {
std::cout << "Called std::vector equality check" << std::endl;
if (n1.size() != n2.size()) {
return false;
}
for (size_t i = 0; i < n1.size(); i++) {
if (!(*n1[i] == *n2[i])) {
return false;
}
}
return true;
}
class Node {
public:
virtual bool operator==(const Node& other) const {
std::cout << "Called Node equality check" << std::endl;
return false;
}
};
class NumberExprNode : public Node {
double Val;
public:
virtual bool operator==(const NumberExprNode& other) const __attribute__((used)) {
std::cout << "Called NumberExprNode equality check" << std::endl;
return (Val == other.Val);
}
NumberExprNode(double val) : Val(val) {}
};
int main() {
std::vector<std::unique_ptr<**Node**>> n_vec1;
n_vec1.emplace_back(std::make_unique<NumberExprNode>(5));
n_vec1.emplace_back(std::make_unique<NumberExprNode>(6));
std::vector<std::unique_ptr<**Node**>> n_vec2;
n_vec2.emplace_back(std::make_unique<NumberExprNode>(5));
n_vec2.emplace_back(std::make_unique<NumberExprNode>(6));
return (n_vec1 == n_vec2);
}
I see the following printed to stdout:
Called std::vector equality check
Called Node equality check
But if I replace ‘main’ with:
int main() {
std::vector<std::unique_ptr<**NumberExprNode**>> n_vec1;
n_vec1.emplace_back(std::make_unique<NumberExprNode>(5));
n_vec1.emplace_back(std::make_unique<NumberExprNode>(6));
std::vector<std::unique_ptr<**NumberExprNode**>> n_vec2;
n_vec2.emplace_back(std::make_unique<NumberExprNode>(5));
n_vec2.emplace_back(std::make_unique<NumberExprNode>(6));
return (n_vec1 == n_vec2);
then I see
Called std::vector equality check
Called NumberExprNode equality check
Called NumberExprNode equality check
printed to stdout, which is what I wanted.
I find this counterintuitive. The entries of n_vec1 each point to objects initialized with the derived class. And I think that the derived class instances have an intact vtable. I stepped through in GDB and printed *n1[i] and *n2[i]. I observed that they each had a vtable for NumberExprNode. So I conclude that even the first example should properly dispatch to the NumberExprNode overridden operator==. Yet is it not. Why is this?
Taking a look at the bigger picture, for my application I want to be able to take in a heterogeneous std::vectorstd::unique_ptr<Node> where each entry could point to a different derived class. I want the equality operator to properly dispatch to the correct operator== method that corresponds to the class that was used to initialize the object. How can I rework/restructure my code to accomplish this?