2

有一个无法修改的旧代码。如果简化它看起来像这样:

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);
}

有没有办法只用 std::variant 和 std::visit 替换 prop1ref 中的属性访问代码?

请注意,prop1 名称和类型在类之间确实匹配,但位置(偏移量)不匹配。类型字段偏移得到保证,因此始终可以进行转换。此外,新代码应允许在不使用宏的情况下访问 A、B、C 类中的双 prop2、字符串 prop3 等。

4

2 回答 2

1

可能是这样的:

#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));
}

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

Demo

于 2021-09-21T06:03:03.813 回答
0

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

#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;
}

现场代码在这里

于 2021-09-21T07:09:55.460 回答