是否有std::variant和std::visit的方式来代替旧的遗留分派混乱的代码?



有不能修改的旧的遗留代码。如果简化,它看起来像这样:

enum class Type {A, B, C};
struct Object {Type type;};
Object* objs[N];
int count = 0;
#define addobj(ptr) objs[count++] = (Object*)ptr
struct A {
Type type;
int prop1;
A(int v) : type(Type::A), prop1(v) {addobj(this);}
};
struct B {
Type type;
int prop1;
B(int v) : type(Type::B), prop1(v) {addobj(this);}
};
struct C {
Type type;
int prop1;
C(int v) : type(Type::C), prop1(v) {addobj(this);}
};
A* getA(int id) {return (A*)objs[id];}
B* getB(int id) {return (B*)objs[id];}
C* getC(int id) {return (C*)objs[id];}

此外,还有"多态";属性访问,允许更改:

int& prop1ref(int id) {
switch (objs[id]->type) {
case Type::A: return getA(id)->prop1;
case Type::B: return getB(id)->prop1;
}
return getC(id)->prop1;
}
void test() {
A a(1); B b(2); C c(3);
for (int id=0; id<count; id++)
prop1ref(id);
}

是否有一种方法来替换prop1ref中的属性访问代码,例如std::variant和std::visit?

请注意,prop1名称和类型在类之间匹配,但位置(偏移量)不匹配。保证类型字段偏移,因此总是可以进行强制转换。此外,新的代码应该允许在不使用宏的情况下访问A, B, C类中的double prop2, string prop3等。

可能是这样的:

#include <variant>
#include <type_traits>
#include <iostream>
// ...
std::variant<A*, B*, C*> fetch_from_id(int const id) {
switch (objs[id]->type) {
case Type::A: return getA(id);
case Type::B: return getB(id);
default: return getC(id);
}
}
void test() {
A a(1); B b(2); C c(3);
for (int id = 0; id < count; id++)
std::visit([] (auto&& v) {
using type = std::decay_t<decltype(v)>;
if constexpr (std::is_same_v<type, A*>) {
// For A
std::cout << v->prop1 << std::endl;
}
if constexpr (std::is_same_v<type, B*>) {
// For B
std::cout << v->prop1 << std::endl;
}
if constexpr (std::is_same_v<type, C*>) {
// For C
std::cout << v->prop1 << std::endl;
}
}, fetch_from_id(id));
}

如果你想亲自看看它是否有效:

下面将允许您使用lambdas以一种非常简单的方式提取其他属性:

#include <variant>
#include <string>
#include <iostream>

enum class Type {A, B, C};
struct Object {Type type;};
Object* objs[3];
int count = 0;
#define addobj(ptr) objs[count++] = (Object*)ptr
struct A {
Type type;
int prop1;
A(int v) : type(Type::A), prop1(v) {addobj(this);}
};
struct B {
Type type;
int prop1;
B(int v) : type(Type::B), prop1(v) {addobj(this);}
};
struct C {
Type type;
int prop1;
C(int v) : type(Type::C), prop1(v) {addobj(this);}
};
A* getA(int id) {return (A*)objs[id];}
B* getB(int id) {return (B*)objs[id];}
C* getC(int id) {return (C*)objs[id];}
using var_t = std::variant<A*,B*,C*>;
var_t fetch_from_id(int const id) {
switch (objs[id]->type) {
case Type::A: return getA(id);
case Type::B: return getB(id);
default: return getC(id);
}
}
// straightforward lambdas for extracting any property from your objects
auto get_prop1 = [](auto&& t){ return t->prop1;};
auto get_prop2 = [](auto&& t){ return t->prop2;};
auto get_prop3 = [](auto&& t){ return t->prop3;};

int main()
{
A a(1); B b(2); C c(3);
// print prop1
for (int id=0; id<3; id++) std::cout << std::visit(get_prop1, fetch_from_id(id)) << " ";
std::cout << std::endl;
// print prop2
// for (int id=0; id<3; id++) std::cout << std::visit(get_prop2, fetch_from_id(id)) << " ";
// std::cout << std::endl;
// print prop3
// for (int id=0; id<3; id++) std::cout << std::visit(get_prop3, fetch_from_id(id)) << " ";
// std::cout << std::endl;
}

Live code here

最新更新