m42e@blog~%

Get rid of unions and some more advantages of std::variant.

Let’s go back to C++17. It is already a while out there and a feature I really like is std::variant. It is handy in a lot of places. In all places you may have used unions before. Having an Interface which accepts a std::variant which it does not use itself, but passes it further makes the interface slimmer and easier. You could have done that with templates, but using std::variant is much more simple.

Let’s look at some code.

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

using namespace std::literals;

void funcb(std::variant<int, std::string>& data){
  if(auto pval = std::get_if<std::string>(&data)){
    std::cout << "a string, how nice: " << pval << std::endl;
  }
  else{
    std::cout << "not a string, something else" << std::endl;
  }
}

void a(std::variant<int, std::string>& data){
  std::cout << "size is " << sizeof(data) << std::endl;
  funcb(data);
}


int main(){
  std::variant<int, std::string> s = "AString"s;
  std::variant<int, std::string> z = 3208;

  a(s);
  a(z);

  return 0;
}

Ok, so we are able to pass things without bothering. I mean in this case it has been easy, we have a string and an integer. So is there another way than these std::get_if? Indeed. Use std::visit.

std::visit in combination with lambdas is really great. You are using templates without a single template parameter. It is so nice, elegant and not too hard.

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

using namespace std::literals;

void a(std::variant<int, std::string>& data){
  std::cout << "size is " << sizeof(data) << std::endl;
  std::visit([](auto&& arg){
    using T = std::decay_t<decltype(arg)>;
    if constexpr (std::is_same_v<T, int>){
        std::cout << "int " << arg << '\n';
    }else if constexpr (std::is_same_v<T, std::string>){
        std::cout << "string " << arg << '\n';
    }
  }, data);
}


int main(){
  std::variant<int, std::string> s = "AString"s;
  std::variant<int, std::string> z = 3208;

  a(s);
  a(z);

  return 0;
}

The lambda in the std::visit is using constexpr. This will reduce the generated functions to a minimum. The lamba here act like a template and we do a specialization there so it handles all types correctly. I found this pretty useful also for some kind of serialization. So you have it all in one place.