使用现代C++中的可选成员进行构造

8 c++ design-patterns c++11

我们继承了旧代码,我们正在转换为现代C++以获得更好的类型安全性,抽象性和其他好处.我们有许多带有许多可选成员的结构,例如:

struct Location {
    int area;
    QPoint coarse_position;
    int layer;
    QVector3D fine_position;
    QQuaternion rotation;
};
Run Code Online (Sandbox Code Playgroud)

重要的是,所有成员都是可选的.至少有一个将出现在任何给定的位置实例中,但不一定全部存在.可能有更多的组合可能比原始设计师显然方便表达每个单独的结构.

结构以这种方式反序列化(伪代码):

Location loc;
// Bitfield expressing whether each member is present in this instance
uchar flags = read_byte();
// If _area_ is present, read it from the stream, else it is filled with garbage
if (flags & area_is_present)
    loc.area = read_byte();
if (flags & coarse_position_present)
    loc.coarse_position = read_QPoint();
etc.
Run Code Online (Sandbox Code Playgroud)

在旧代码中,这些标志永久存储在结构中,并且每个结构成员的getter函数在运行时测试这些标志,以确保所请求的成员存在于给定的Location实例中.

我们不喜欢这种运行时检查系统.请求不存在的成员是一个严重的逻辑错误,我们希望在编译时找到它.这应该是可能的,因为无论何时读取位置,都知道应该存在哪些成员变量组合.

首先,我们考虑使用std :: optional:

struct Location {
    std::optional<int> area;
    std::optional<QPoint> coarse_location;
    // etc.
};
Run Code Online (Sandbox Code Playgroud)

该解决方案使设计缺陷现代化而不是修复它.

我们想过像这样使用std :: variant:

struct Location {
    struct Has_Area_and_Coarse {
        int area;
        QPoint coarse_location;
    };
    struct Has_Area_and_Coarse_and_Fine {
        int area;
        QPoint coarse_location;
        QVector3D fine_location;
    };
    // etc.
    std::variant<Has_Area_and_Coarse,
                 Has_Area_and_Coarse_and_Fine /*, etc.*/> data;
};
Run Code Online (Sandbox Code Playgroud)

当多个成员变量组合成为可能时,此解决方案使非法状态无法表示,但无法很好地扩展.此外,我们不希望通过指定Has_Area_and_Coarse来访问,而是通过更接近loc.fine_position的访问.

我们没有考虑过这个问题的标准解决方案吗?

Yol*_*ola 3

那么 mixin 呢?

struct QPoint {};
struct QVector3D {};
struct Area {
    int area;
};
struct CoarsePosition {
    QPoint coarse_position;
};
struct FinePosition {
    QVector3D fine_position;
};
template <class ...Bases>
struct Location : Bases... {
};

Location<Area, CoarsePosition> l1;
Location<Area, FinePosition> l2;
Run Code Online (Sandbox Code Playgroud)