使用带有提升变体的声明递归

Recursive using declaration with boost variant

本文关键字:声明 递归      更新时间:2023-10-16

我试图表示一种树状递归数据结构,其中每个节点可能是两种不同的数据类型之一。我使用boost变体来"容纳"每个节点上可能存在的两种类型。

但是,我遇到了一个问题。我严格使用"using"指令声明所有这些类型,因此当我了解节点的递归性质时,它失败了,因为 typedef/using 可能不使用递归。

如何做到这一点?

using LeafData = int; // just for illustration
using LeafNode = std::unordered_map<std::string, LeafData>;
using InnerNode = std::unordered_map<std::string, boost_variant<InnerNode, LeafNode>>; // this is problematic since I'm using InnerNode recursively

我已经探索了使用 boost::make_recursive_variant 但它创建的类型 (A( 并不完全是我需要的,因为这会在变体中产生变体,而我想要一个由 InnerNode 或 LeafNode 组成的 (B( 单个变体。

(A) boost_variant<boost_variant<InnerNode, LeafNode>, LeafNode>
(B) boost_variant<InnerNode, LeafNode>

直接答案(提示见下文(

您可以使用make_recursive_variant做您想做的事:

住在科里鲁

#include <boost/variant.hpp>
#include <unordered_map>
struct LeafData {
    int _i;
    LeafData(int i) : _i(i) {}
}; // just for illustration
using LeafNode = std::unordered_map<std::string, LeafData>;
using Node = boost::make_recursive_variant<
        LeafNode,
        std::unordered_map<std::string, boost::recursive_variant_>
    >::type;
using Inner = std::unordered_map<std::string, Node>;
int main() {
    Node tree = Inner {
        { "a", LeafNode { { "one", 1 }, { "two", 2 }, { "three",3 } } },
        { "b", Inner {
                { "b1", LeafNode { { "four", 4 }, { "five", 5 }, { "six", 6 } } },
                { "b2", LeafNode { { "seven", 7 }, { "eight", 8 }, { "nine", 9 } } },
            }
        },
        { "c", LeafNode {} },
    };
}

技巧

为什么要区分内部/叶子节点?在我看来,叶节点只是具有值而不是子节点的节点:

住在科里鲁

#include <boost/variant.hpp>
#include <unordered_map>
struct Data {
    int _i;
    Data(int i) : _i(i) {}
}; // just for illustration
using Tree = boost::make_recursive_variant<
        Data,
        std::unordered_map<std::string, boost::recursive_variant_>
    >::type;
using Node = std::unordered_map<std::string, Tree>;
int main() {
    Tree tree = Node {
        { "a", Node { { "one", 1 }, { "two", 2 }, { "three",3 } } },
        { "b", Node {
                { "b1", Node { { "four", 4 }, { "five", 5 }, { "six", 6 } } },
                { "b2", Node { { "seven", 7 }, { "eight", 8 }, { "nine", 9 } } },
            }
        },
        { "c", Node {} },
    };
}

没有制造递归变体

你可以用一个判断良好的前向声明:

住在科里鲁

#include <boost/variant.hpp>
#include <unordered_map>
struct Data {
    int _i;
    Data(int i) : _i(i) {}
}; // just for illustration
struct Node;
using Tree = boost::variant<Data, boost::recursive_wrapper<Node> >;
struct Node : std::unordered_map<std::string, Tree> {
    using base = std::unordered_map<std::string, Tree>;
    using base::base; // inherit constructor
};
int main() {
    Tree tree = Node {
        { "a", Node { { "one", 1 }, { "two", 2 }, { "three",3 } } },
        { "b", Node {
                { "b1", Node { { "four", 4 }, { "five", 5 }, { "six", 6 } } },
                { "b2", Node { { "seven", 7 }, { "eight", 8 }, { "nine", 9 } } },
            }
        },
        { "c", Node {} },
    };
}

更优雅 + 更高效

如果您使用可在映射类型仍未完成时实例化的unordered_map¹,则根本不需要recursive_wrapper的性能影响。

在这个过程中,我们可以使构造函数更智能,树结构更简洁:

住在科里鲁

#include <boost/variant.hpp>
#include <boost/unordered_map.hpp>
struct Data {
    int _i;
    Data(int i = 0) : _i(i) {}
}; // just for illustration
struct Node : boost::variant<Data, boost::unordered_map<std::string, Node> > {
    using Map = boost::unordered_map<std::string, Node>;
    using Base = boost::variant<Data, Map>;
    using Base::variant;
    using Base::operator=;
    Node(std::initializer_list<Map::value_type> init) : Base(Map(init)) {}
};
int main() {
    auto tree = Node {
        { "a", { { "one", 1 }, { "two", 2 }, { "three", 3 } } },
        { "b", {
                { "b1", { { "four", 4 }, { "five", 5 }, { "six", 6 } } },
                { "b2", { { "seven", 7 }, { "eight", 8 }, { "nine", 9 } } },
            }
        },
        { "c", {} },
    };
}

¹(我认为 c++17 将其添加到标准库规范中(

您将需要一个指针,该变体要么是叶数据,要么是指向另一个叶的指针。

我能够获得 boost::make_recursive_variant 方法的工作,但它需要对变体访问者进行额外的处理程序。

using LeafData = int; // just for illustration
using LeafNode = std::unordered_map<std::string, LeafData>;
using InnerNode = std::unordered_map<std::string, boost::make_recursive_variant<boost::recursive_variant_, LeafNode>::type>;

变体访问者需要处理三种类型,LeafNode,InnerNode,AND,InnerNode::mapped_type(这是递归变体(,然后编译器似乎很高兴。不确定这是否会"工作",所以需要运行并查看行为。