std::variant反射。我如何确定std::variant分配的值的类型?

34

我有一个名为foo_t的类,其中有一个成员叫做bar,它可以是std::stringintstd::vector<double>等类型之一。我想要能够询问foo_t已经分配给bar哪种类型。我决定使用std::variant

我编写了一个解决方案,但我不确定这是否是std::variant的好用法。我不确定是否重要,但我希望类型列表可能在未来增长得更多。我创建了一个枚举类来存储std::variant分配给哪种类型。我的第一个实现也可以在wandbox上找到:

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

enum foo_kind_t {
  double_list,
  name_tag,
  number,
  unknown
};

template <typename val_t>
struct get_foo_kind_t {
  constexpr static foo_kind_t value = unknown;
};

template <>
struct get_foo_kind_t<int> {
  constexpr static foo_kind_t value = number;
};

template <>
struct get_foo_kind_t<std::string> {
  constexpr static foo_kind_t value = name_tag;
};

template <>
struct get_foo_kind_t<std::vector<double>> {
  constexpr static foo_kind_t value = double_list;
};

class foo_t {

public:

  foo_t(): kind(unknown) {}

  template <typename val_t>
  void assign_bar(const val_t &val) {
    static_assert(get_foo_kind_t<val_t>::value != unknown, "unsupported assignment");
    kind = get_foo_kind_t<val_t>::value;
    bar = val;
  }

  foo_kind_t get_kind() {
    return kind;
  }

  template <typename val_t>
  val_t get_bar() {
    if (get_foo_kind_t<val_t>::value != kind) {
      throw std::runtime_error("wrong kind");
    }
    return std::get<val_t>(bar);
  }

private:

  foo_kind_t kind;

  std::variant<
    int,
    std::string,
    std::vector<double>
  > bar;

};

template <typename val_t>
void print_foo(foo_t &foo) {
    std::cout << "kind: " << foo.get_kind() << std::endl;
    std::cout << "value: " << foo.get_bar<val_t>() << std::endl << std::endl;
}

int main(int, char*[]) {

    // double_list
    foo_t b;
    std::vector<double> b_val({ 1.0, 1.1, 1.2 });
    b.assign_bar(b_val);
    std::cout << "kind: " << b.get_kind() << std::endl;
    std::cout << "value: vector size: " << b.get_bar<std::vector<double>>().size() << std::endl << std::endl;

    // name_tag
    foo_t d;
    std::string d_val("name");
    d.assign_bar(d_val);
    print_foo<std::string>(d);

    // number
    foo_t c;
    int c_val = 99;
    c.assign_bar(c_val);
    print_foo<int>(c);

    // unknown
    foo_t a;
    std::cout << a.get_kind() << std::endl;

    return 0;
}

这是一个好的做法吗?有没有更高效的方法?有没有需要写更少代码的方法?有没有不需要使用C++17的方法?


2
你为什么需要知道那个? - juanchopanza
2
我有一棵树,其中包含许多类型为 foo_t 的节点,每个节点都包含自己的 0 个或多个子节点,这些子节点也是类型为 foo_t 的。我想在遍历此树的同时,基于这些节点绘制用户界面。我知道我可以使用访问者模式来实现,但我仍然希望能够动态获取类型。 - Hoodlum
7
std::holds_alternative - Vencat
3个回答

36
如果你只需要针对单一类型X询问“这是类型X的变体吗?”,我建议你优先使用 std::holds_alternative 而不是 std::variant::index,因为代码行更易读且在将来类型在变体中的索引发生更改时不必更新。

例如:

if (std::holds_alternative<X>(my_variant)) {
    std::cout << "Variant is of type X" << std::endl;
}

太好了!当 std::variant 模板参数被更改时,使用索引会产生很多副作用,这种方法比使用索引好多了。 - slinkin

23

1

使用类型特征可以解决这个问题

    #include <iostream>
    #include <string>
    #include <type_traits>
    #include <variant>
    
    using MyVariant = std::variant<int, std::string>;
    
    enum class MyVariantType { integer, string };
    
    template <MyVariantType Type, typename T> struct is_variant_type : std::false_type {};
    
    template <> struct is_variant_type<MyVariantType::integer, int        > : std::true_type {};
    template <> struct is_variant_type<MyVariantType::string , std::string> : std::true_type {};
    
    template<MyVariantType VT>
    bool check_variant_type(const MyVariant& myvar)
    {
        return std::visit([&](const auto& arg) { 
            return is_variant_type<VT, std::decay_t<decltype(arg)>>::value;
        }, myvar);
    }
    
    int main(int argc, char* argv[])
    {
        MyVariant a = int(10);
        MyVariant b = "Hello";
    
        std::cout << check_variant_type<MyVariantType::integer>(a);
        std::cout << check_variant_type<MyVariantType::integer>(b);
    
        std::cout << check_variant_type<MyVariantType::string>(a);
        std::cout << check_variant_type<MyVariantType::string>(b);
    
        return 0;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接