替代使用回调函数从构造函数调用 virtual/derived 方法?
Alternative to calling virtual/derived methods from constructor using callback function?
我遇到了上述问题。我想构建一棵具有关联节点的树。由于所有树的行为都相同但类型不同,本着继承的精神,我希望能够使用由基 class 定义的相同树构造例程构造具有不同类型的树。我想知道适合我的情况的最佳做法是什么。
struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };
Tree()
{
root_ = CreateNode();
// carry on adding nodes to their parents...
}
virtual std::shared_ptr<Node> CreateNode() { return std::shared_ptr<Node>(new Node()); }
std::shared_ptr<Node> root_;
};
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived() : Tree() {}
virtual std::shared_ptr<Node> CreateNode() { return std::shared_ptr<NodeDerived>(new NodeDerived()); }
};
问题是在构造基(显然)之前我无法调用派生函数,并且它使用 CreateNode
方法的基实现,该方法始终使用基节点实现构造树。这意味着我可以在不延迟树木数量的情况下建造树木。显而易见的解决方案是对树进行模板化以采用不同的节点类型,使用特征强制节点类型?但是,这也意味着所有方法的定义都必须在 header? class 非常多,所以我想尽可能避免这种情况,所以我考虑传递一个 lambda 来为我做这件事。
struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };
Tree(std::function<std::shared_ptr<Node>()> customNodeConstructor)
{
root_ = customNodeConstructor();
// carry on adding nodes to their parents... using the customNodeConstructor to create the nodes.
}
std::shared_ptr<Node> root_;
};
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived(std::function<std::shared_ptr<Node>()> customNodeConstructor) : Tree(customNodeConstructor) {}
};
这允许我派生并传递与派生树相关的 customNodeConstructor
。类型安全是部分强制执行的,因为返回的 shared_ptr
object 必须派生自 Tree::Node
,尽管不强制执行派生节点类型。
即 TreeDerived
实例化,它也许应该使用 TreeDerived::NodeDerived
只强制使用 Tree::Node
或派生类型,但不一定 TreeDerived::NodeDerived
。
然后可以像这样使用...
Tree tree([]() { return std::shared_ptr<Tree::Node>(); });
TreeDerived treeDerived([]() { return std::shared_ptr<TreeDerived::NodeDerived>(); });
这是好的做法还是我应该做 more/something 而不是模板化树 object?
非常感谢。
想法不错;但是,在派生的 class 中创建 "custom tree constructor" 而不是在外部创建它会更安全。这样,您就不会得到不正确的节点类型。代码形式:
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived() : Tree([]() { return std::make_shared<NodeDerived>(); }) {}
};
另请注意,在一般情况下,std::function
会为每次调用带来不小的运行时开销。如果您总是要传递无状态的 lambda,请考虑在 Tree
构造函数中采用普通函数指针:
Tree(std::shared_ptr<Node> (*customNodeConstructor)())
{
root_ = customNodeConstructor();
// carry on adding nodes to their parents... using the customNodeConstructor to create the nodes.
}
作为此 "pass in a customised creator" 方法的替代方法,您还可以仅将 Tree
的构造函数转换为函数模板。它可能看起来像这样:
template <class T> struct TypeTag;
struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };
template <class ConcreteNode>
Tree(TypeTag<ConcreteNode>)
{
root_ = std::make_shared<ConcreteNode>();
// carry on adding nodes to their parents...
}
std::shared_ptr<Node> root_;
};
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived() : Tree(TypeTag<NodeDerived>{}) {}
};
然后必须在某个地方定义构造函数模板,所有从 Tree
派生的 classes 都可以看到它的定义(很可能在头文件中),但是 Tree
class 保持正常。
我很难理解你为什么需要你做的设计(struct inside derived struct 派生自 struct inside base class 至少可以说是有问题的)。
话虽如此,您可以考虑 workarounds 来自 mclow 的常见问题解答。
tl;dr 是您将逻辑从构造函数移动到 init() 和 init() 虚函数调用内部 "work".
我遇到了上述问题。我想构建一棵具有关联节点的树。由于所有树的行为都相同但类型不同,本着继承的精神,我希望能够使用由基 class 定义的相同树构造例程构造具有不同类型的树。我想知道适合我的情况的最佳做法是什么。
struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };
Tree()
{
root_ = CreateNode();
// carry on adding nodes to their parents...
}
virtual std::shared_ptr<Node> CreateNode() { return std::shared_ptr<Node>(new Node()); }
std::shared_ptr<Node> root_;
};
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived() : Tree() {}
virtual std::shared_ptr<Node> CreateNode() { return std::shared_ptr<NodeDerived>(new NodeDerived()); }
};
问题是在构造基(显然)之前我无法调用派生函数,并且它使用 CreateNode
方法的基实现,该方法始终使用基节点实现构造树。这意味着我可以在不延迟树木数量的情况下建造树木。显而易见的解决方案是对树进行模板化以采用不同的节点类型,使用特征强制节点类型?但是,这也意味着所有方法的定义都必须在 header? class 非常多,所以我想尽可能避免这种情况,所以我考虑传递一个 lambda 来为我做这件事。
struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };
Tree(std::function<std::shared_ptr<Node>()> customNodeConstructor)
{
root_ = customNodeConstructor();
// carry on adding nodes to their parents... using the customNodeConstructor to create the nodes.
}
std::shared_ptr<Node> root_;
};
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived(std::function<std::shared_ptr<Node>()> customNodeConstructor) : Tree(customNodeConstructor) {}
};
这允许我派生并传递与派生树相关的 customNodeConstructor
。类型安全是部分强制执行的,因为返回的 shared_ptr
object 必须派生自 Tree::Node
,尽管不强制执行派生节点类型。
即 TreeDerived
实例化,它也许应该使用 TreeDerived::NodeDerived
只强制使用 Tree::Node
或派生类型,但不一定 TreeDerived::NodeDerived
。
然后可以像这样使用...
Tree tree([]() { return std::shared_ptr<Tree::Node>(); });
TreeDerived treeDerived([]() { return std::shared_ptr<TreeDerived::NodeDerived>(); });
这是好的做法还是我应该做 more/something 而不是模板化树 object?
非常感谢。
想法不错;但是,在派生的 class 中创建 "custom tree constructor" 而不是在外部创建它会更安全。这样,您就不会得到不正确的节点类型。代码形式:
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived() : Tree([]() { return std::make_shared<NodeDerived>(); }) {}
};
另请注意,在一般情况下,std::function
会为每次调用带来不小的运行时开销。如果您总是要传递无状态的 lambda,请考虑在 Tree
构造函数中采用普通函数指针:
Tree(std::shared_ptr<Node> (*customNodeConstructor)())
{
root_ = customNodeConstructor();
// carry on adding nodes to their parents... using the customNodeConstructor to create the nodes.
}
作为此 "pass in a customised creator" 方法的替代方法,您还可以仅将 Tree
的构造函数转换为函数模板。它可能看起来像这样:
template <class T> struct TypeTag;
struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };
template <class ConcreteNode>
Tree(TypeTag<ConcreteNode>)
{
root_ = std::make_shared<ConcreteNode>();
// carry on adding nodes to their parents...
}
std::shared_ptr<Node> root_;
};
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived() : Tree(TypeTag<NodeDerived>{}) {}
};
然后必须在某个地方定义构造函数模板,所有从 Tree
派生的 classes 都可以看到它的定义(很可能在头文件中),但是 Tree
class 保持正常。
我很难理解你为什么需要你做的设计(struct inside derived struct 派生自 struct inside base class 至少可以说是有问题的)。
话虽如此,您可以考虑 workarounds 来自 mclow 的常见问题解答。
tl;dr 是您将逻辑从构造函数移动到 init() 和 init() 虚函数调用内部 "work".