我有以下代码。有一个基类 (
Node
) 和一个派生类 (NumberExprNode
),每个类都有一个虚拟相等方法。
#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; // Base class Node used here
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; // Base class Node used here
n_vec2.emplace_back(std::make_unique<NumberExprNode>(5));
n_vec2.emplace_back(std::make_unique<NumberExprNode>(6));
return (n_vec1 == n_vec2);
}
我看到以下内容打印到标准输出:
Called std::vector equality check
Called Node equality check
但是如果我将“main”替换为:
int main() {
std::vector<std::unique_ptr<NumberExprNode>> n_vec1; // Derived class used here
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; // Derived class used here
n_vec2.emplace_back(std::make_unique<NumberExprNode>(5));
n_vec2.emplace_back(std::make_unique<NumberExprNode>(6));
return (n_vec1 == n_vec2);
然后我明白了
Called std::vector equality check
Called NumberExprNode equality check
Called NumberExprNode equality check
打印到标准输出,这就是我想要的。
我觉得这违反直觉。 n_vec1 的每个条目都指向使用派生类初始化的对象。我认为派生类实例具有完整的 vtable。我在 GDB 中单步执行并打印了
*n1[i]
和 *n2[i]
。我观察到他们每个人都有一个 NumberExprNode
的 vtable。所以我得出的结论是,即使是第一个示例也应该正确分派到被覆盖的 NumberExprNode
。但事实并非如此。这是为什么?纵观全局,对于我的应用程序,我希望能够采用异构的 operator==
,其中每个条目都可以指向不同的派生类。我希望相等运算符正确分派到与用于初始化对象的类相对应的正确
std::vector<std::unique_ptr<Node>>
方法。我如何修改/重组我的代码来实现这一目标?operator==
所以
class NumberExprNode : public Node {
double Val;
public:
virtual bool operator==(const Node& other_) const override {
std::cout << "Called NumberExprNode equality check" << std::endl;
const auto& other=dynamic_cast<const NumberExprNode&>(other_);
return (Val == other.Val);
}
NumberExprNode(double val) : Val(val) {}
};
实际上会覆盖其他
operator==
:operator==
但是,如果您将
stieber@gatekeeper:~ $ g++ Test.cpp; ./a.out
Called std::vector equality check
Called NumberExprNode equality check
Called NumberExprNode equality check
与仅
NumberExprNode
进行比较,上面的代码将会抛出异常,因为转换将会失败。所以,你可以尝试这样的事情:
Node
这将允许测试适用于所有组合 - 然而,我在这里担心的是,我不记得在任何地方实际这样做过,因为它基本上是尝试实现不同类的比较。