std::variant 个具有相同碱基的 class 个 class
std::variant of classes with same base class
我没有完全理解变体的用法,也许有人可以弄清楚我做错了什么(可能是我的方法)。
设变体对象是两个 class 的 _Types,它们都继承相同的 class。
class base
{
public:
int foo;
};
class a: public base
{
int bar;
};
class b: public base
{
float bar;
};
byte variant_id = 0; // 0 for class A, 1 for class B
std::variant< base, a, b > variant;
以下是我使用变体的方式:
void func( )
{
auto& _variant = std::get< base >( variant ); // ideally would be = variant_id ? std::get< b >( variant ) : std::get< a >( variant )
_variant.foo = 20;
if ( variant_id == 1 )
{
auto& variant_ = std::get< b >( variant );
variant_.bar = 20.f;
}
else
{
auto& variant_ = std::get< a >( variant );
variant_.bar = 20;
}
也许工会更有效?
union
{
a _a;
b _b;
} variant;
byte variant_id = 0;
std::variant
为您做的是跟踪当前(最后分配的)类型是什么,并在您尝试获取不同的类型时发出警告。因此,您应该使用 variant.index()
.
而不是跟踪 variant_id
此外,我相信如果分配的类型不是 base
类型,您对 base
的第一次提取实际上会失败。假设你的对象总是 a
或 b
类型(永远不会 base
),你应该从变体类型构造函数中删除 base
类型。
我在这里假设您不是在创建 类 base
、a
和 b
自己(并且不能触摸它们),因此虚拟方法方法不可行。
Variant
知道它存储的是哪种类型。 Union
希望您在外部跟踪该类型。如果您尝试访问 variant
中的错误项,您会得到 exception
或 nullptr
,对于 Union
这只是未定义的行为。
()
Koehler 对您使用中的一些技术错误给出了很好的回答,但我觉得变体是不适合这里工作的工具。
通常,您会为不相关的数据类型使用 std::variant
。有理由在这里使用变体吗?由于您只持有 base
的子 class,您通常会选择 std::unique_ptr<base>
或 std::shared_ptr<base>
(取决于要求)并完成它。
我看到使用子classes 的变体的唯一原因是确保它们可以连续存储以减少 memory/indirection 成本。即使那样,我也会通过基础 class 界面使用它,如下所示:
base& getBase(std::variant<a, b>& v)
{
// the conditional here might be omitted in the generated
// code since the branches might be identical
return v.index() == 0 ? std::get<a>(v) : std::get<b>(v);
}
// use like
base& b = getBase(variant);
b.func(20);
尽量不要查询变体的类型。如果你这样做,你的代码本质上等同于 if 链中的一堆动态转换,代码味道。
相反,让变体为您发货。如果您想访问 a
和 b
的公共 base
,则不需要该变体中的 base
成员。
使用访客
std::variant< a, b > var;
void func( )
{
std::visit([](auto&& v) {
v.foo = 20; // Both `a` and `b` have a foo, this is well formed.
v.bar = 20; // Both have a `bar` that can be assigned a 20. This is well formed too
}, var);
}
我没有完全理解变体的用法,也许有人可以弄清楚我做错了什么(可能是我的方法)。
设变体对象是两个 class 的 _Types,它们都继承相同的 class。
class base
{
public:
int foo;
};
class a: public base
{
int bar;
};
class b: public base
{
float bar;
};
byte variant_id = 0; // 0 for class A, 1 for class B
std::variant< base, a, b > variant;
以下是我使用变体的方式:
void func( )
{
auto& _variant = std::get< base >( variant ); // ideally would be = variant_id ? std::get< b >( variant ) : std::get< a >( variant )
_variant.foo = 20;
if ( variant_id == 1 )
{
auto& variant_ = std::get< b >( variant );
variant_.bar = 20.f;
}
else
{
auto& variant_ = std::get< a >( variant );
variant_.bar = 20;
}
也许工会更有效?
union
{
a _a;
b _b;
} variant;
byte variant_id = 0;
std::variant
为您做的是跟踪当前(最后分配的)类型是什么,并在您尝试获取不同的类型时发出警告。因此,您应该使用 variant.index()
.
variant_id
此外,我相信如果分配的类型不是 base
类型,您对 base
的第一次提取实际上会失败。假设你的对象总是 a
或 b
类型(永远不会 base
),你应该从变体类型构造函数中删除 base
类型。
我在这里假设您不是在创建 类 base
、a
和 b
自己(并且不能触摸它们),因此虚拟方法方法不可行。
Variant
知道它存储的是哪种类型。 Union
希望您在外部跟踪该类型。如果您尝试访问 variant
中的错误项,您会得到 exception
或 nullptr
,对于 Union
这只是未定义的行为。
(
Koehler 对您使用中的一些技术错误给出了很好的回答,但我觉得变体是不适合这里工作的工具。
通常,您会为不相关的数据类型使用 std::variant
。有理由在这里使用变体吗?由于您只持有 base
的子 class,您通常会选择 std::unique_ptr<base>
或 std::shared_ptr<base>
(取决于要求)并完成它。
我看到使用子classes 的变体的唯一原因是确保它们可以连续存储以减少 memory/indirection 成本。即使那样,我也会通过基础 class 界面使用它,如下所示:
base& getBase(std::variant<a, b>& v)
{
// the conditional here might be omitted in the generated
// code since the branches might be identical
return v.index() == 0 ? std::get<a>(v) : std::get<b>(v);
}
// use like
base& b = getBase(variant);
b.func(20);
尽量不要查询变体的类型。如果你这样做,你的代码本质上等同于 if 链中的一堆动态转换,代码味道。
相反,让变体为您发货。如果您想访问 a
和 b
的公共 base
,则不需要该变体中的 base
成员。
使用访客
std::variant< a, b > var;
void func( )
{
std::visit([](auto&& v) {
v.foo = 20; // Both `a` and `b` have a foo, this is well formed.
v.bar = 20; // Both have a `bar` that can be assigned a 20. This is well formed too
}, var);
}