在 C++ 中向多态树添加功能
adding functionality onto polymorphic tree in c++
我有一个多态树,我正在尝试添加功能,例如比较两个节点而不使用像 dynamic_cast
这样的 RTTI,我采用的方法是访问者模式。
我遇到的问题是访问者模式不允许我操作任何参数或从函数中获取 return 类型。
例如,如果我想编写一个比较两个节点的访问者
class AbstractDispatch{
public:
virtual void visit(NodeFoo &operand) = 0;
virtual void visit(NodeBar &operand) = 0;
//...for all types.
};
class CompareVisitor{
public:
void visit(NodeFoo &operand) override;
//...
};
class SetVisitor{
public:
void visit(NodeFoo &operand) override;
//...
};
void CompareVisitor::visit(NodeFoo &operand){
//compare operand to what?
//get result of comparison how?
}
void SetVisitor::visit(NodeFoo &operand){
//set operand to what?
}
我目前的想法是给访问者添加其他功能和成员类。这会让我做这样的事情:
Base *object = new NodeFoo();
CompareVisitor compare;
compare.set_parameters(NodeFoo(/* */));
object->accept(compare);
bool result = compare.get_result();
我可以设置比较访问者的参数并遍历树,检查节点并以这种方式执行其他此类操作。
另一种解决方案是在节点中存储节点类型信息并执行 get_type() 检查是否安全。
dynamic_cast
很慢,但如果节点类型层次结构非常简单,它会更快吗?有没有更好的设计模式来做这样的事情?
您可以编写一个访问者,将当前节点与前一个节点进行比较。
class Node{...}
class NodeFoo : public Node {...}
class NodeBar : public Node {...}
class visitor{
public:
void visit( const NodeFoo& node ){
//First node: store relevant comparison information to private variables
if( foo == nullptr ){
foo = &node;
}
//Other nodes: Compare to the stored data and store comparison result
else {
...
}
}
void visit( const NodeBar& node ){
...
}
bool result() const{ return result; };
private:
bool result = false;
//variables for comparison, could be this simple but also a variant type
// or plain Node*
NodeFoo* foo = nullptr;
NodeBar* bar = nullptr;
}
你会像这样使用它
Node node1;
Node node2;
Visitor v;
node1.accept( v );
node2.accept( v );
v.result();
当然,这是一个非常基本的实现,如果您假设所有访问过的节点都具有相同的类型,您可以使用普通的 Node* 来存储第一个节点。您还可以使用变体类型或将类型存储为字符串...(您可以通过执行的访问函数知道类型)
如果节点生命周期不确定,您可以存储一些节点相关数据,您需要比较而不是指向节点的指针....有数百种可能性,这只是一个小草图基本框架
如果允许 CompareVisitor
有一个状态,可以用双 visit
(= 两个虚拟调用)来完成。考虑到您的访客 API.
,我认为没有解决方法
#include <iostream>
#include <string>
#include <vector>
#include <memory>
struct FooNode;
struct BarNode;
struct Visitor
{
virtual void visit(FooNode&)=0;
virtual void visit(BarNode&)=0;
};
struct Node{
virtual void accept(Visitor& v) = 0;
};
struct FooNode: public Node{
virtual void accept(Visitor& v) override { v.visit(*this);}
const char* print(){return "FooNode";}
};
struct BarNode: public Node{
virtual void accept(Visitor& v) override { v.visit(*this);}
const char* print(){return "BarNode";}
};
using ret_type=std::string;
//Feel free to specialize or overload
//Or create comparator class that allows partial specializations
template<typename Left, typename Right>
ret_type compare(Left &left, Right& right){
return std::string(left.print()) + "<=>" + right.print() + '\n';
}
//Compares (visited) and (rightNode)
class RightCompareVisitor : public Visitor {
public:
RightCompareVisitor(Node& right):rightNode(right){}
void visit(FooNode &left) override
{
visitRightNode(left);
}
void visit(BarNode &left) override
{
visitRightNode(left);
}
ret_type getRes() { return std::move(result);}
private:
template<typename Left>
void visitRightNode(Left& left){
struct CompareVisitor: Visitor
{
ret_type& result;
Left& left;
CompareVisitor(ret_type& result, Left& left):result(result), left(left){}
void visit(FooNode &right) override final{
result = compare(left, right);
}
void visit(BarNode &right) override final{
result = compare(left, right);
}
};
CompareVisitor v(result, left);
rightNode.accept(v);
}
ret_type result;
Node& rightNode;
};
//If you add this then you can always just use 'compare' to compare any two
//nodes.
template<>
ret_type compare<Node,Node>(Node& left, Node& right){
RightCompareVisitor rC{right};
left.accept(rC);
return rC.getRes();
}
int main()
{
std::vector<std::unique_ptr<Node>> nodes;
nodes.emplace_back(std::make_unique<FooNode>());
nodes.emplace_back(std::make_unique<BarNode>());
nodes.emplace_back(std::make_unique<FooNode>());
for(auto&& left : nodes)
for(auto&& right: nodes)
std::cout<<compare(*left,*right);
}
在你想要的地方添加 const
s。如果您希望 RightCompareVisitor
被重用,则对节点使用指针。
输出:
FooNode<=>FooNode
FooNode<=>BarNode
FooNode<=>FooNode
BarNode<=>FooNode
BarNode<=>BarNode
BarNode<=>FooNode
FooNode<=>FooNode
FooNode<=>BarNode
FooNode<=>FooNode
我有一个多态树,我正在尝试添加功能,例如比较两个节点而不使用像 dynamic_cast
这样的 RTTI,我采用的方法是访问者模式。
我遇到的问题是访问者模式不允许我操作任何参数或从函数中获取 return 类型。
例如,如果我想编写一个比较两个节点的访问者
class AbstractDispatch{
public:
virtual void visit(NodeFoo &operand) = 0;
virtual void visit(NodeBar &operand) = 0;
//...for all types.
};
class CompareVisitor{
public:
void visit(NodeFoo &operand) override;
//...
};
class SetVisitor{
public:
void visit(NodeFoo &operand) override;
//...
};
void CompareVisitor::visit(NodeFoo &operand){
//compare operand to what?
//get result of comparison how?
}
void SetVisitor::visit(NodeFoo &operand){
//set operand to what?
}
我目前的想法是给访问者添加其他功能和成员类。这会让我做这样的事情:
Base *object = new NodeFoo();
CompareVisitor compare;
compare.set_parameters(NodeFoo(/* */));
object->accept(compare);
bool result = compare.get_result();
我可以设置比较访问者的参数并遍历树,检查节点并以这种方式执行其他此类操作。
另一种解决方案是在节点中存储节点类型信息并执行 get_type() 检查是否安全。
dynamic_cast
很慢,但如果节点类型层次结构非常简单,它会更快吗?有没有更好的设计模式来做这样的事情?
您可以编写一个访问者,将当前节点与前一个节点进行比较。
class Node{...}
class NodeFoo : public Node {...}
class NodeBar : public Node {...}
class visitor{
public:
void visit( const NodeFoo& node ){
//First node: store relevant comparison information to private variables
if( foo == nullptr ){
foo = &node;
}
//Other nodes: Compare to the stored data and store comparison result
else {
...
}
}
void visit( const NodeBar& node ){
...
}
bool result() const{ return result; };
private:
bool result = false;
//variables for comparison, could be this simple but also a variant type
// or plain Node*
NodeFoo* foo = nullptr;
NodeBar* bar = nullptr;
}
你会像这样使用它
Node node1;
Node node2;
Visitor v;
node1.accept( v );
node2.accept( v );
v.result();
当然,这是一个非常基本的实现,如果您假设所有访问过的节点都具有相同的类型,您可以使用普通的 Node* 来存储第一个节点。您还可以使用变体类型或将类型存储为字符串...(您可以通过执行的访问函数知道类型)
如果节点生命周期不确定,您可以存储一些节点相关数据,您需要比较而不是指向节点的指针....有数百种可能性,这只是一个小草图基本框架
如果允许 CompareVisitor
有一个状态,可以用双 visit
(= 两个虚拟调用)来完成。考虑到您的访客 API.
#include <iostream>
#include <string>
#include <vector>
#include <memory>
struct FooNode;
struct BarNode;
struct Visitor
{
virtual void visit(FooNode&)=0;
virtual void visit(BarNode&)=0;
};
struct Node{
virtual void accept(Visitor& v) = 0;
};
struct FooNode: public Node{
virtual void accept(Visitor& v) override { v.visit(*this);}
const char* print(){return "FooNode";}
};
struct BarNode: public Node{
virtual void accept(Visitor& v) override { v.visit(*this);}
const char* print(){return "BarNode";}
};
using ret_type=std::string;
//Feel free to specialize or overload
//Or create comparator class that allows partial specializations
template<typename Left, typename Right>
ret_type compare(Left &left, Right& right){
return std::string(left.print()) + "<=>" + right.print() + '\n';
}
//Compares (visited) and (rightNode)
class RightCompareVisitor : public Visitor {
public:
RightCompareVisitor(Node& right):rightNode(right){}
void visit(FooNode &left) override
{
visitRightNode(left);
}
void visit(BarNode &left) override
{
visitRightNode(left);
}
ret_type getRes() { return std::move(result);}
private:
template<typename Left>
void visitRightNode(Left& left){
struct CompareVisitor: Visitor
{
ret_type& result;
Left& left;
CompareVisitor(ret_type& result, Left& left):result(result), left(left){}
void visit(FooNode &right) override final{
result = compare(left, right);
}
void visit(BarNode &right) override final{
result = compare(left, right);
}
};
CompareVisitor v(result, left);
rightNode.accept(v);
}
ret_type result;
Node& rightNode;
};
//If you add this then you can always just use 'compare' to compare any two
//nodes.
template<>
ret_type compare<Node,Node>(Node& left, Node& right){
RightCompareVisitor rC{right};
left.accept(rC);
return rC.getRes();
}
int main()
{
std::vector<std::unique_ptr<Node>> nodes;
nodes.emplace_back(std::make_unique<FooNode>());
nodes.emplace_back(std::make_unique<BarNode>());
nodes.emplace_back(std::make_unique<FooNode>());
for(auto&& left : nodes)
for(auto&& right: nodes)
std::cout<<compare(*left,*right);
}
在你想要的地方添加 const
s。如果您希望 RightCompareVisitor
被重用,则对节点使用指针。
输出:
FooNode<=>FooNode
FooNode<=>BarNode
FooNode<=>FooNode
BarNode<=>FooNode
BarNode<=>BarNode
BarNode<=>FooNode
FooNode<=>FooNode
FooNode<=>BarNode
FooNode<=>FooNode