msg
msg

Reputation: 166

Weird result of std::variant

I'm struggling with an odd behavior of the below code. The problem is that output of this snippet is "double". But we have an int in lhs.

I dug for a while and figured out the compiler put a garbage double value into var for some reason. Then this turned the type of the variable into double. This breaks all my code at the outer scope, because I do some operations according to the type of the variable.

What am I missing? Is it supposed to work like this?

std::variant<int, double> lhs = 4;

auto var = std::holds_alternative<int>(lhs) ? std::get<int>(lhs) : std::get<double>(lhs);

if (std::strcmp(typeid(var).name(), "i") == 0)
    std::cout << "int";
else
    std::cout << "double";

Upvotes: 1

Views: 113

Answers (2)

Vlad from Moscow
Vlad from Moscow

Reputation: 311088

In this declaration:

auto var = std::holds_alternative<int>(lhs) ? std::get<int>(lhs) : std::get<double>(lhs);

The conditional operator is used. The compiler determines the type of the result based on the types of the 2nd and 3rd operands of the operator. Due to the usual arithmetic conversions, the common type is double. That is (from the C++20 Standard):

  • Otherwise, if either operand is double, the other shall be converted to double

Upvotes: 5

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275800

std::variant<int, double> lhs = 4;

so lhs is a variant holding either an int or a double. As you stored an int in it, it holds an int.

auto var = std::holds_alternative<int>(lhs) ? std::get<int>(lhs) : std::get<double>(lhs);

this is the same as

auto var = true ? std::get<int>(lhs) : std::get<double>(lhs);

which is

auto var = true ? (int)4 : double(3.14);

The type of var is computed to be double, but its value is 4.

double var = 4;

auto is not a runtime dynamic type; it is a compile time computed type.

if (std::strcmp(typeid(var).name(), "i") == 0)
  std::cout << "int";
else
  std::cout << "double";

which prints "double".

I think what you want to do is this:

std::variant<int, double> lhs = 4;

std::visit( [&](auto var){
  if (std::strcmp(typeid(var).name(), "i") == 0)
    std::cout << "int";
  else
    std::cout << "double";
}, lhs);

which prints "int" as expected.

Here, we create a visitor function. std::visit makes a runtime switch to decide which type lhs contains, and calls the one that matches the type within lhs.

Be careful, because both versions of the lambda function are instantiated, but only one is called.

Upvotes: 6

Related Questions