Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在现代C+中具有可选成员的结构+; 我们继承了旧代码,我们正在转换为现代C++,以获得更好的类型安全、抽象和其他优点。我们有许多具有许多可选成员的结构,例如: struct Location { int area; QPoint coarse_position; int layer; QVector3D fine_position; QQuaternion rotation; };_C++_C++11_Design Patterns - Fatal编程技术网

在现代C+中具有可选成员的结构+; 我们继承了旧代码,我们正在转换为现代C++,以获得更好的类型安全、抽象和其他优点。我们有许多具有许多可选成员的结构,例如: struct Location { int area; QPoint coarse_position; int layer; QVector3D fine_position; QQuaternion rotation; };

在现代C+中具有可选成员的结构+; 我们继承了旧代码,我们正在转换为现代C++,以获得更好的类型安全、抽象和其他优点。我们有许多具有许多可选成员的结构,例如: struct Location { int area; QPoint coarse_position; int layer; QVector3D fine_position; QQuaternion rotation; };,c++,c++11,design-patterns,C++,C++11,Design Patterns,重要的一点是,所有成员都是可选的。在任何给定的位置实例中至少会出现一个,但不一定全部。可能会有更多的组合,这显然比最初的设计人员发现的使用单独的结构来表达更方便 结构以这种方式反序列化(伪代码): 在旧代码中,这些标志永久存储在结构中,每个结构成员的getter函数在运行时测试这些标志,以确保请求的成员存在于给定的位置实例中 我们不喜欢这种运行时检查系统。请求一个不存在的成员是一个严重的逻辑错误,我们希望在编译时发现这个错误。这应该是可能的,因为无论何时读取位置,都知道应该存在哪个成员变量组合

重要的一点是,所有成员都是可选的。在任何给定的位置实例中至少会出现一个,但不一定全部。可能会有更多的组合,这显然比最初的设计人员发现的使用单独的结构来表达更方便

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

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

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

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

struct Location {
    std::optional<int> area;
    std::optional<QPoint> coarse_location;
    // etc.
};
结构位置{
std:可选区域;
std::可选的粗略位置;
//等等。
};
此解决方案使设计缺陷现代化,而不是修复它

我们考虑使用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;
};
结构位置{
结构具有面积和粗糙度{
内部区域;
QPoint粗化_位置;
};
结构有_区域_和_粗_和_细{
内部区域;
QPoint粗化_位置;
QVector3D精细定位;
};
//等等。
std::变量数据;
};
这种解决方案使得非法状态无法表示,但当成员变量的组合可能超过几个时,无法很好地扩展。此外,我们不希望通过指定Has\u Area\u和\u rough来访问,而是通过更接近loc.fine\u位置的方式来访问

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

关于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;
structqpoint{};
结构QVector3D{};
结构区{
内部区域;
};
结构粗定位{
QPoint粗齌位置;
};
结构精细定位{
QVector3D精细位置;
};
模板
结构位置:基础。。。{
};
位置l1;
位置l2;

首先我要说的是,我偶尔也想对一个类进行“可选化”,其中所有成员都是可选的。我在想,如果没有使用类似Antony Polukhin的代码进行适当的元编程,这可能是可能的

尽管如此。。。您可以拥有具有任意类型值的部分类型安全属性映射:

class Location {
    enum class Attribute { area, coarse_position, fine_position, layer };   
    std::unoredered_map<Attribute, std::any> attributes;
}
属性的使用可能涉及检查相关属性是否存在的自由函数

在实践中,顺便说一句,
std::vector
可能比
std::unordered_map
搜索得更快


警告:此解决方案没有提供您所需的类型安全性。

您可以拥有使位图在编译时进行检查的结构版本。我假设对于一段特定的代码,您可以对当前的内容进行假设。在该代码中,您可以使用编译时位图获取版本。为了成功地将运行时位映射版本转换为编译时位映射,将验证位映射

#include <stdexcept>

struct foo
{
    int a;
    float b;
    char c;
};

struct rt_foo : foo
{
    unsigned valid;
};

template <unsigned valid>
struct ct_foo : foo
{
    // cannnot default construct
    ct_foo () = delete;

    // cannot copy from version withouth validity flags
    ct_foo (foo const &) = delete;
    ct_foo & operator = (foo const &) = delete;

    // copying from self is ok
    ct_foo (ct_foo const &) = default;
    ct_foo & operator = (ct_foo const &) = default;

    // converting constructor and assignement verify the flags 
    ct_foo (rt_foo const & rtf) :
        foo (check (rtf))
    {
    }

    ct_foo & operator = (rt_foo const & rtf)
    {
        *static_cast <foo*> (this) = check (rtf);

        return *this;
    }

    // using a member that is not initialize will be a compile time error at when
    // instantiated, which will occur at the time of use

    auto & get_a () { static_assert (valid & 1); return a; }
    auto & get_b () { static_assert (valid & 2); return a; }
    auto & get_c () { static_assert (valid & 3); return a; }

    // helper to validate the runtime conversion

    static foo & check (rt_foo const & rtf)
    {
        if ((valid & rtf.valid) != 0)
            throw std::logic_error ("bad programmer!");
    }
};
#包括
结构foo
{
INTA;
浮球b;
字符c;
};
结构rt_foo:foo
{
无符号有效;
};
模板
结构ct_foo:foo
{
//不能使用默认构造
ct_foo()=删除;
//无法从没有有效性标志的版本复制
ct_foo(foo const&)=删除;
ct_foo&运算符=(foo const&)=删除;
//自我复制是可以的
ct_foo(ct_foo const&)=默认值;
ct\u foo&运算符=(ct\u foo const&)=默认值;
//转换构造函数和赋值验证标志
ct_foo(rt_foo const&rtf):
foo(支票(rtf))
{
}
ct_-foo和运算符=(rt_-foo常量和rtf)
{
*静态(本)=检查(rtf);
归还*这个;
}
//当使用未初始化的成员时,将出现编译时错误
//实例化,将在使用时发生
auto&get_a(){static_assert(valid&1);返回a;}
auto&get_b(){static_assert(valid&2);返回a;}
auto&get_c(){static_assert(valid&3);返回a;}
//帮助程序来验证运行时转换
静态foo&check(rt\u foo const&rtf)
{
如果((有效&rtf.valid)!=0)
抛出std::logic_错误(“糟糕的程序员!”);
}
};

如果您总是知道在读取或构造时会出现哪些字段,那么将有效位设为模板参数并使用
static\u assert检查将起作用

#include <stdexcept>
#include <iostream>

struct stream {
    template <typename value> value read ();
    template <typename value> void read (value &);
};

template <unsigned valid>
struct foo
{
    int a;
    float b;
    char c;

    auto & get_a () { static_assert (valid & 1); return a; }
    auto & get_b () { static_assert (valid & 2); return b; }
    auto & get_c () { static_assert (valid & 4); return c; }
};

template <unsigned valid>
foo <valid> read_foo (stream & Stream)
{
    if (Stream.read <unsigned> () != valid)
        throw std::runtime_error ("unexpected input");

    foo <valid> Foo;

    if (valid & 1) Stream.read (Foo.a);
    if (valid & 2) Stream.read (Foo.b);
    if (valid & 4) Stream.read (Foo.c);
}

void do_something (stream & Stream)
{
    auto Foo = read_foo <3> (Stream);

    std::cout << Foo.get_a () << ", " << Foo.get_b () << "\n";

    // don't touch c cause it will fail here
    // Foo.get_c ();
}

我觉得这个代码的设计有问题。类字段本身不能是可选的,即使所有字段都以某种方式包装,那么使用此类结构的所有代码都将被可选性检查弄得乱七八糟。不太清楚为什么您会将其视为设计缺陷。您能给出由于这种设计而产生的问题的具体示例吗?如果您想防止请求未初始化的成员,最简单的解决方案就是使用一个初始化所有成员的构造函数。有没有理由不使用?如果您想要更现代的方法,元组还可以与std::get结合使用,以访问在运行时推出的成员。如果您不想“请求不存在的成员”,您应该为每个可能的字段组合定义单独的类(如果有意义,可以使用类层次结构),在基类((纯)虚拟)中定义所需的操作,并在派生类中实现它们。那么你就不再需要任何运行时检查了。但是虚拟函数调用可能比simp慢一点
#include <stdexcept>

struct foo
{
    int a;
    float b;
    char c;
};

struct rt_foo : foo
{
    unsigned valid;
};

template <unsigned valid>
struct ct_foo : foo
{
    // cannnot default construct
    ct_foo () = delete;

    // cannot copy from version withouth validity flags
    ct_foo (foo const &) = delete;
    ct_foo & operator = (foo const &) = delete;

    // copying from self is ok
    ct_foo (ct_foo const &) = default;
    ct_foo & operator = (ct_foo const &) = default;

    // converting constructor and assignement verify the flags 
    ct_foo (rt_foo const & rtf) :
        foo (check (rtf))
    {
    }

    ct_foo & operator = (rt_foo const & rtf)
    {
        *static_cast <foo*> (this) = check (rtf);

        return *this;
    }

    // using a member that is not initialize will be a compile time error at when
    // instantiated, which will occur at the time of use

    auto & get_a () { static_assert (valid & 1); return a; }
    auto & get_b () { static_assert (valid & 2); return a; }
    auto & get_c () { static_assert (valid & 3); return a; }

    // helper to validate the runtime conversion

    static foo & check (rt_foo const & rtf)
    {
        if ((valid & rtf.valid) != 0)
            throw std::logic_error ("bad programmer!");
    }
};
#include <stdexcept>
#include <iostream>

struct stream {
    template <typename value> value read ();
    template <typename value> void read (value &);
};

template <unsigned valid>
struct foo
{
    int a;
    float b;
    char c;

    auto & get_a () { static_assert (valid & 1); return a; }
    auto & get_b () { static_assert (valid & 2); return b; }
    auto & get_c () { static_assert (valid & 4); return c; }
};

template <unsigned valid>
foo <valid> read_foo (stream & Stream)
{
    if (Stream.read <unsigned> () != valid)
        throw std::runtime_error ("unexpected input");

    foo <valid> Foo;

    if (valid & 1) Stream.read (Foo.a);
    if (valid & 2) Stream.read (Foo.b);
    if (valid & 4) Stream.read (Foo.c);
}

void do_something (stream & Stream)
{
    auto Foo = read_foo <3> (Stream);

    std::cout << Foo.get_a () << ", " << Foo.get_b () << "\n";

    // don't touch c cause it will fail here
    // Foo.get_c ();
}
template <unsigned valid>
void print_foo (std::ostream & os, foo <valid> const & Foo)
{
    if constexpr (valid & 1)
        os << "a = " << Foo.get_a () << "\n";
    if constexpr (valid & 2)
        os << "b = " << Foo.get_b () << "\n";
    if constexpr (valid & 4)
        os << "c = " << Foo.get_c () << "\n";
}