9

过去,我使用 gcc 的C99 风格的C++ 复合文字扩展来编码代码中的嵌套常量数据结构。这是一个例子:

#include <iostream>
using namespace std;

struct Tree {
    const char *name;
    const Tree *left;
    const Tree *right;
};

const Tree *const tree = (Tree []) {
    "top", // name
    (Tree[]) {
        "left",
        0,
        0
    },
    (Tree[]) {
        "right",
        0,
        0
    }
};

static void dump(const Tree *tree) {
    if (!tree) {
        cout << "null";
        return;
    }

    cout << tree->name << "(";
    dump(tree->left);
    cout << ", ";
    dump(tree->right);
    cout << ")";
}

int main(void) {
    dump(tree);
    cout << "\n";
}

这个想法是为这些相当大的常量结构使用静态存储持续时间,初始化成本为零,并且除非需要,否则实际上不需要将任何内容分页到内存中。

但是,这在最新版本的 clang 中不再有效,并且最新的 OS X 以“gcc”的名义捆绑了 clang。所以我需要一个不同的解决方案。

在 C++ 中,最好的符合标准的习惯用法是什么?

我并不特别想支付在这些结构中构建所有对象的成本,所以如果可以避免这种情况,那就太好了。

4

2 回答 2

5

C++11统一初始化语法应该可以工作:

const Tree* const tree = new Tree{"top",
    new Tree{"left", nullptr, nullptr},
    new Tree{"right", nullptr, nullptr}
};

否则,只需创建一个将名称和子树作为参数的构造函数。


如果您不希望动态分配结构,则必须自己创建每个结构,然后使用例如 address-of 运算符将它们链接在一起:

namespace
{
    const Tree leftTree{"left", nullptr, nullptr};
    const Tree rightTree{"right", nullptr, nullptr};
    const Tree topTree{"top", &leftTree, &rightTree};
}

const Tree* const tree = &topTree;
于 2013-10-25T09:47:04.070 回答
4

如果大型复杂类型不是递归的,您可以简单地使用 constexpr 类型和统一初始化而无需任何技巧。

struct B { int i; };
struct C { double d; };

struct A {
  B b;
  C c;
};

constexpr A {B{1},C{3.2}};

然而,由于它是一棵树,而且你不能只拥有这样的递归类型(因为它的大小将是无限的),所以技巧是必要的。我能想到两种方法。首先是使用指针或引用以避免无限递归。

使用指针,您需要一种创建静态对象并获取指向它们的指针的方法。我不认为 C++ 有任何东西可以让您在单个表达式中执行此操作,因此需要为树中的每个节点声明,这很不方便。

对于引用,您需要某种方式来表示一个空节点(因为如果没有危险的黑客攻击,引用本身就不能为空)。这是一个简单的实现:

struct Tree {
    const char *name;
    Tree const &left;
    Tree const &right;
};

constexpr Tree Null{nullptr,Null,Null};

void print_tree(Tree const &t) {
  if (&t == &Null) {
    std::cout << "()";
    return;
  }
  std::cout << '(' << t.name << ", ";
  print_tree(t.left);
  std::cout << ", ";
  print_tree(t.right);
  std::cout << ")";
}

constexpr Tree a {"a",
                  Tree{"b",
                       Null,
                       Tree{"d",Null,Null}},
                  Tree{"c",Null,Null}};

int main() {
  print_tree(a);
}

避免递归的第二种方法是使用模板为每个不同的树结构生成不同的类型。

template<typename LTree, typename RTree>
struct Tree {
    const char *name;
    LTree left;
    RTree right;
};

struct null_tree_t {};
constexpr null_tree_t null_tree{};

template<typename RTree>
struct Tree<null_tree_t, RTree> {
    const char *name;
    RTree right;
};

template<typename LTree>
struct Tree<LTree, null_tree_t> {
    const char *name;
    LTree left;
};

template<>
struct Tree<null_tree_t, null_tree_t> {
    const char *name;
};

// C++14 return type deduction
template<typename LTree, typename RTree>
constexpr auto make_tree(const char *name, LTree ltree, RTree rtree) {
    return Tree<LTree, RTree>{name, ltree, rtree};
}

template<typename LTree>
constexpr auto make_tree(const char *name, LTree ltree) {
    return Tree<LTree, null_tree_t>{name, ltree};
}

template<typename RTree>
constexpr auto make_tree(const char *name, null_tree_t, RTree rtree) {
    return Tree<null_tree_t, RTree>{name, rtree};
}

constexpr auto make_tree(const char *name) {
    return Tree<null_tree_t, null_tree_t>{name};
}

template<typename LTree, typename RTree>
void print(Tree<LTree, RTree> const &tree) {
  std::cout << '{' << tree.name << ", ";
  print(tree.left);
  std::cout << ", ";
  print(tree.right);
  std::cout << '}';
}

template<typename LTree>
void print(Tree<LTree, null_tree_t> const &tree) {
  std::cout << '{' << tree.name << ", ";
  print(tree.left);
  std::cout << ", {}}";
}

template<typename RTree>
void print(Tree<null_tree_t, RTree> const &tree) {
  std::cout << '{' << tree.name << ", {}, ";
  print(tree.right);
  std::cout << "}";
}

void print(Tree<null_tree_t, null_tree_t> const &tree) {
  std::cout << '{' << tree.name << "}";
}

constexpr auto a = make_tree("a",
                             make_tree("b",
                                       null_tree,
                                       make_tree("d")),
                             make_tree("c"));

int main() {
  print(a);    
}

这样,叶节点具有 type Tree<null_tree_t, null_tree_t>,具有作为叶节点的左孩子Tree< Tree<null_tree_t, null_tree_t>, null_tree_t>的树是 ,具有作为叶节点的右孩子的左孩子的树是:

Tree<
  Tree<
    null_tree_t,
    Tree<
      null_tree_t,
      null_tree_t>>,
  null_tree_t>

等等。

于 2013-10-25T18:05:10.793 回答