【问题标题】:How can I emulate a recursive type definition in C++?如何在 C++ 中模拟递归类型定义?
【发布时间】:2011-06-29 22:16:39
【问题描述】:

昨天我问了以下question,为了方便,这里转载;

“对于我的一个项目,我真正想做的是这个(将其简化到最低限度);

struct Move
{
    int src;
    int dst;
};

struct MoveTree
{
    Move move;
    std::vector<MoveTree> variation;
};

我必须承认,我认为直接执行此操作是不可能的,我认为 MoveTree 中的 MoveTree 向量将被禁止。但无论如何我都试过了,效果很好。我正在使用 Microsoft Visual Studio 2010 Express。

这是便携的吗?这是好习惯吗?我有什么需要担心的吗?”

基本上社区的回答是否定的,我不能这样做,标准禁止这样做,所以它有效的事实意味着我只是幸运。

所以我的新问题是。如何在合法的 C++ 中实现我想要的简单功能,而又不增加一大堆令人讨厌的复杂性和痛苦?

【问题讨论】:

  • 你也可以使用recursive_wrapper,但是如果和vector一起使用的话效率很低,因为堆流量很大。
  • 你为什么删除了@Johannes 的答案?这是一种与@Cat Plus Plus 不同的方法,有一些优点和缺点:优点是您可以提供对向量的引用并保持用户代码基本不变,缺点是向量增长的处理成本更高.
  • @David 因为我觉得这里 recursive_wrapper 不好用-.-

标签: c++ design-patterns


【解决方案1】:

您需要使用指针和动态分配。你应该使用智能指针,以确保你不会泄漏任何东西。 boost::shared_ptr 允许类型不完整,因此这是合法的:

std::vector< boost::shared_ptr<MoveTree> > variation;

(我不知道 0x std::shared_ptr TBH,但应该是一样的)。

【讨论】:

  • 我认为这仍然是未定义的,无论是否合理:见 - stackoverflow.com/questions/6517231/…
  • @MerlynMorgan-Graham:使用指针绝对不是未定义的。
  • 但它是指针吗?查看另一个问题的答案并告诉我您可以使用不完整类型作为模板参数(使用限定符,它仅在内部用作指针)。标准中的措辞似乎非常绝对。或者,如果这是格式正确的,那么我认为std::vector&lt;MoveTree&gt; 也会是格式正确的。
  • @MerlynMorgan-Graham:您不能使用不完整的类型作为标准库容器的模板参数(17.4 是“库范围的要求”部分)。没有语言规则阻止您编写适用于不完整类型的模板——boost::shared_ptr 明确允许不完整类型——请参阅 Boost 文档。
  • 明白了。然后为您的答案 +1 :)
【解决方案2】:

您可以使用Boost Pointer Container Library。它类似于使用 std::vector,但容器拥有指针,因此它将销毁对象。

你可以声明它:

#include <boost/ptr_container/ptr_vector.hpp>
struct MoveTree{
  Move move;
  boost::ptr_vector<MoveTree> variation;
};

现在,如果你想添加一个新元素,你可以使用:

variation.push_back(new MoveTree());

现在,容器拥有指针,您可以通过引用获得它,例如:

variation[i].move.src = ...

容器被销毁时对象也被销毁。

【讨论】:

【解决方案3】:

我手头没有 C++ 标准的副本,因此无法检查标准,但问题是:

  • 类不能包含自己的具体实例,即使是传递性的——例如,struct foo { foo x; } 是非法的,原因很明显。更一般地说,除了成员函数的主体之外,您不能在其任何成员中引用结构的大小。
  • 类可以包含指向自身的指针 - struct foo { foo *x; } 非常好

所以问题是,std::vector 是否定义了任何直接或间接依赖于sizeof(MoveTree) 的成员(成员函数除外)?

显然,std::vector 本身不能有一个静态成员 MoveTree,因为这意味着空向量调用 MoveTree 构造函数。然而,人们可以设想一个 std::vector 对单元素向量进行对齐的char inlineStorage[sizeof(MoveTree)] 优化。暂且不说这是否会提高性能,手头的问题是标准是否允许实现采用这种方法。

也就是说,由于不同的原因,这仍然是一个坏主意:因为向量在调整其存储大小时必须复制其元素,所以在向量中包含具有昂贵复制构造函数的元素是一个坏主意。这里我们有一个类,它的复制构造函数必须递归地重新创建整个树。最好使用智能指针类来间接引用子节点以避免这种开销:

std::vector<boost::shared_ptr<MoveTree> > variation;
// in C++0x:
std::vector<std::shared_ptr<MoveTree> > variation;
// in C++0x you can also use for lower overhead:
std::vector<std::unique_ptr<MoveTree> > variation;
// you must then use this pattern to push:

variation.push_back(std::move(std::unique_ptr<MoveTree>(new MoveTree())));

【讨论】:

  • 好吧,我已经使用我的代码几个月了,没有任何问题。我不太担心性能。而这种“推动模式”正是我希望避免的那种令人讨厌的复杂性。仍然 +1 提供一些想法谢谢。
  • 更具体地说,问题是标准禁止实例化任何具有不完整类型的标准容器。
【解决方案4】:

如果您可以在 any 模板中使用不完整类型 MoveTree 作为模板参数,则使用其他答案中的一种解决方案(例如 Cat Plus Plus 的),或者直接使用您的原始解决方案,添加一些沉重的 cmets,稍后再进行忏悔。

如果您不能将它用作 any 模板参数,而它仍然不完整,您可以使用pimpl idiom 来解决这个问题。

在定义实现类时,MoveTree 类将完成:

struct Move
{
    int src;
    int dst;
};

struct MoveTreeImpl;

struct MoveTree
{
    Move move;

    // Todo: Implement RAII here for the impl
    // Todo: Provide impl accessor functions here

private:
    MoveTreeImpl* impl;
};

struct MoveTreeImpl
{
    std::vector<MoveTree> variation;
};

这个解决方案有很多毛病:

由于您要避免直​​接实例化任何类型不完整的模板,因此您必须手动实现 RAII。你不会从std::scoped_ptr&lt;MoveTreeImpl&gt; 那里得到帮助,因为MoveTreeImpl 也不完整。

您的访问器函数有什么签名?你能从访问者那里返回std::vector&lt;MoveTree&gt;&amp; 吗?我对此不确定——我们试图避免直接使用MoveTree 的模板。这可能会有所不同,因为它不是数据成员,但我不确定:)

编辑:

阅读更多内容,并从其他用户那里获得回复,似乎很多这种废话都是不必要的。只有标准容器有限制。您可以使用std:scoped_ptr&lt;MoveTreeImpl&gt; 实现pimpl,我认为从访问器函数返回std::vector&lt;MoveTree&gt;&amp;,两者都没有问题。

【讨论】:

    【解决方案5】:

    使用指向 Vector 中类型的指针(或智能指针),这将是可移植的。这是因为指向类型的指针只需声明即可完成,您不需要定义。

    struct Move
        {
            int src;
            int dst;
        };
    
    struct MoveTree;
    
    struct MoveTree
        {
            Move move;
            std::vector<MoveTree*> variation;
        };
    

    如果您需要管理类型,请使用可以为您处理删除的智能指针。

    原答案to this question

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-10
      • 1970-01-01
      • 1970-01-01
      • 2020-12-06
      • 2017-07-26
      • 2021-09-06
      • 2017-02-13
      • 1970-01-01
      相关资源
      最近更新 更多