3

I am trying to access method of class held in std::variant. Unfortunately it throws and following error:

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

Code:

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

I tried to read documentation however I still don't understand how or whether should I use std::get() and std::variants.index() to access the aforementioned method.

After some reading I tried doing something like this in loop:

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

It still didn't work though. The error was:

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:
MrSuhar
  • 47
  • 6
  • 3
    Yes, you need to use `std::get`. – HolyBlackCat Jun 26 '20 at 11:42
  • The active type of the variant is a runtime property, so one cannot directly invoke member functions on the variant and expect them to be forwarded to the active member, because such invocation is a compile-time thing. – underscore_d Jun 26 '20 at 12:06
  • Looks to me like you're attempting to learn how to use variants without [helpful assistance from a good C++ textbook](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list), which should have many examples of how to set and access members of variants. This is basic variant usage that I expect to be covered in every C++ textbook. Unfortunately, stackoverflow.com is not a very efficient replacement for a textbook. – Sam Varshavchik Jun 26 '20 at 12:16

3 Answers3

3

To initialize a value in std::variant you can use std::get, or since C++17, emplace():

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

To access a value, use std::get:

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

In addition, the template argument of std::get must be known at compile-time, so std::get<t> when t is a variable, won't work. You can do it slightly differently, e.g. like this:

std::size_t t = array[0][0].index();
switch (t) {
case 0:
    std::get<0>(array[0][0]).function(3);
case 1:
    // . . .
}
rustyx
  • 80,671
  • 25
  • 200
  • 267
1

std::get is the solution you are looking for.

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

Or

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

One more thing, you can use std::array instead this C array, and to get more standard library functionality:

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

The usage can still be the same, or a little more safer:

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

Or:

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

The usage in this case of operator[] for the array might not throw an error in case of wrong index. You better use .at() function, that will throw an exception in case of out of range access:

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

Read about std::out_of_range.


EDIT - template argument deduction/substitution failed

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

You cannot use non const value as template arguments. Template arguments are analyzed during the compilation time, and here t is known only at runtime. Please refer to the following question: Trying to return the value from std::variant using std::visit and a lambda expression

Read more about variants

Coral Kashri
  • 3,436
  • 2
  • 10
  • 22
1

you call the get method giving as parameter the variant and specifying the class for the template

ex: suppose you have a point class with x,y coordinates then std::get(v) will allow you to access the point in the variant

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&) {}
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97