我没有完全理解变体的用法,也许有人可以弄清楚我做错了什么(可能是我的方法)。
让变体对象为两个类的_Types,它们都继承同一个类。
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;
答案 0 :(得分:5)
请尽量不要查询变体的类型。如果这样做,您的代码本质上就等同于if链中的一堆动态转换,这就是代码的味道。
相反,让变体为您调度。而且,如果您要访问base
和a
中的公用b
,则在该变体中不需要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);
}
答案 1 :(得分:1)
std::variant
对您的作用是跟踪当前(最后分配的)类型,并在尝试获取其他类型时抱怨。因此,您应该使用variant_id
。而不是跟踪variant.index()
。
我还相信,如果分配的类型不是base
类型,则您对base
的首次提取实际上将失败。假设您的对象始终是a
或b
类型(而不是base
),则应从变量类型构造函数中删除base
类型。
我在这里假设您不是自己创建类base
,a
和b
(并且不能触摸它们),因此虚拟方法不是可行。
答案 2 :(得分:1)
Koehler对于您使用中的一些技术错误给出了很好的答案,但是我觉得在这里,变体是错误的工具。
通常,您将对不相关的数据类型使用std::variant
。这里是否有理由使用变体?由于您仅持有base
的子类,因此通常会选择std::unique_ptr<base>
或std::shared_ptr<base>
(取决于要求)并完成此操作。
我看到使用子类变体的唯一原因是确保可以连续存储子类以减少内存/间接成本。即便如此,我还是会通过基类接口使用它,就像这样:
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);
答案 3 :(得分:0)
Variant
知道它存储哪种类型。 Union
希望您在外部跟踪该类型。如果尝试访问variant
中的错误项目,则会得到exception
或nullptr
,对于Union
来说,这只是未定义的行为。
(ref)