【问题标题】:How to access method of class stored in std::variant如何访问存储在 std::variant 中的类的方法
【发布时间】:2020-06-26 11:40:38
【问题描述】:

我正在尝试访问 std::variant 中保存的类的方法。不幸的是,它抛出并出现以下错误:

class ’std::variant<A, int>’ has no member named ‘function’

代码:

class A {
private:
    int number = 0;
public:
    A() {};
    void function(int i) {
        number += i;
    }
};

// ... main ...
std::variant<A,B> array[4][4];

array[0][0] = A(){};
array[0][0].function(3);

我尝试阅读文档,但我仍然不明白我应该如何或是否应该使用 std::get() 和 std::variants.index() 来访问上述方法。

经过阅读后,我尝试在循环中做这样的事情:

std::size_t t=array[i][j].index();
std::get<t>(array[i][j]).function();

它仍然没有工作。错误是:

note: candidate: template<class _Tp, class ... _Types> constexpr _Tp&& std::get(std::variant<_Types ...>&&)
     constexpr inline _Tp&& get(variant<_Types...>&& __v)

template argument deduction/substitution failed:

【问题讨论】:

  • 是的,你需要使用std::get
  • 变体的活动类型是运行时属性,因此不能直接调用变体上的成员函数并期望将它们转发给活动成员,因为这种调用是编译时的事情。
  • 在我看来,您正在尝试学习如何在没有helpful assistance from a good C++ textbook 的情况下使用变体,这应该有很多关于如何设置和访问变体成员的示例。这是我希望在每本 C++ 教科书中涵盖的基本变体用法。不幸的是,stackoverflow.com 并不是教科书的有效替代品。

标签: c++ c++17 sfml variant


【解决方案1】:

要在std::variant 中初始化一个值,您可以使用std::get,或者从C++17 开始,emplace()

array[0][0].emplace<A>();

要访问一个值,请使用std::get

std::get<A>(array[0][0]).function(3);

另外,std::get 的模板参数必须在编译时知道,所以当t 是变量时,std::get&lt;t&gt; 将不起作用。你可以做的稍微不同,例如像这样:

std::size_t t = array[0][0].index();
switch (t) {
case 0:
    std::get<0>(array[0][0]).function(3);
case 1:
    // . . .
}

【讨论】:

  • 非常感谢。我有一些新问题,在编辑版本中进行了解释。
  • 为什么要开启索引而不是visit
【解决方案2】:

您调用 get 方法,将变量作为参数并指定模板的类

例如:假设您有一个带有 x,y 坐标的点类 然后 std::get(v) 将允许您访问变体中的点

std::variant<int, Point> v;
v = 177;
int i = std::get<int>(v);
v = Point(15,18);

try 
{
  Point s = std::get<Point>(v);
  std::cout << s.getX();
}
catch (const std::bad_variant_access&) {}

【讨论】:

  • 非常感谢。我有一些新问题,在编辑版本中进行了解释。
【解决方案3】:

std::get 是您正在寻找的解决方案。

std::get<A>(array[0][0]) = AA();
std::get<A>(array[0][0]).function(3);

或者

std::get<int>(array[0][0]) = 56;

还有一点,您可以使用std::array 代替这个C 数组,并获得更多标准库功能:

std::array<std::array<std::variant<A,int>, 4>, 4> array;

用法仍然可以相同,或者更安全一点:

std::get<A>(std::get<0>(std::get<0>(array))) = A();

或者:

std::get<A>(array.at(0).at(0)) = A();

在这种情况下,operator[] 用于数组的用法可能不会在索引错误的情况下引发错误。你最好使用.at()函数,如果超出范围访问会抛出异常:

try {
    std::get<A>(array.at(0).at(0)) = A();
} catch (const std::out_of_range& e) {
    std::cout << e.what() << std::endl;
}

了解std::out_of_range


编辑 - 模板参数扣除/替换失败

std::size_t t = array[i][j].index();
std::get<t>(array[i][j]).function(); // Error!

您不能使用非常量值作为模板参数。模板参数在编译期间进行分析,这里t 仅在运行时才知道。请参考以下问题:Trying to return the value from std::variant using std::visit and a lambda expression

Read more about variants

【讨论】:

  • 非常感谢。我有一些新问题,在编辑版本中进行了解释。
猜你喜欢
  • 2019-05-25
  • 2017-01-10
  • 1970-01-01
  • 2022-01-01
  • 2023-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-03
相关资源
最近更新 更多