什么是代数数据类型的惯用现代C++?

bli*_*ppy 28 c++ polymorphism variant algebraic-data-types unions

例如,假设您要在C++中实现电子表格Cell.单元格可以是字符串,数字,也可以是空的.忽略其他情况,例如它是一个公式.

在Haskell中,您可能会执行以下操作:

data Cell = CellStr String | CellDbl Double | None
Run Code Online (Sandbox Code Playgroud)

什么被认为是在C++中实现它的当前"最佳实践"?在带有类型指示符的结构中使用union,还是其他什么?

Ric*_*ges 21

struct empty_type {};
using cell_type = boost::variant<std::string, double, empty_type>;
Run Code Online (Sandbox Code Playgroud)

然后你会用单元格做一些事情:

boost::apply_visitor(some_visitor(), cell);
Run Code Online (Sandbox Code Playgroud)

  • "LEWG选择不引入明确的附加变体状态,表示其无效(可能是空的,默认构造)状态." (4认同)
  • @filipos在我看来,他们是混合的担忧.可选是一个问题,变体是另一个.如果提议者想要一个可选的变体,他可以使用可选的<variant <... >>.永远不应该允许变体无效,即使在移动之后 - 它应该只包含一个移动的T. (3认同)
  • 另请注意,有一个标准化[`std :: variant`]的建议(http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0088r1.html)(原始提案[此处] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4218.pdf)) (2认同)
  • 最新版本没有强制要求.要使用空状态,请将类型`monostate`显式添加到类型列表中.确实,虽然变体在异常条件下可能变得无效(非空). (2认同)

Fra*_*fer 5

遗产?

我不得不说我不喜欢这种方法而且不会认为它是现代的,但它似乎仍然是标准的.

class DoubleCell : public Cell {
    double value;

    public:
    DoubleCell( double v ) : value(v) {}
    double DoubleValue() { return value; }
    ...
};

class StringCell : public Cell {
    std::string value;

    public:
    StringCell( std::string v ) : value(v) {}
    std::string StringValue() { return value; }
    ...
};

class EmptyCell : public Cell {
    ...
};
Run Code Online (Sandbox Code Playgroud)

一些缺点是:

  • 获取实际值时,需要使用不同的功能.这通常涉及使用instanceof和铸造.

  • 不同的对象不能直接放入容器中,只能作为指针.

  • 我不认为应该删除答案.这是一个明智的设计解决方案,值得讨论,即使只是说有更好的解决方案. (3认同)
  • 每个单元格的指针语义,动态内存分配和虚函数调用对我来说似乎不是一个好主意. (2认同)
  • 模板不会真正起作用(至少在示例中实现时),因为每个单元的类型都需要在编译时知道.我想你可以通过使模板派生自一个公共基类来结合这两种方法,但它会与第一种方法具有相同的性能开销. (2认同)
  • @FrankPuffer使用库只是为了让他们不必自己动手.在您的示例中,您将如何从单元格中获取值?直到你得到它,这真的只是答案的一半. (2认同)