I feel like I'm missing something obvious about int type promotion when assigning to a variant.
On gcc version 9.3.0 (Ubuntu 9.3.0-11ubuntu0~18.04.1), compiling with -std=c++17, the following code fails to compile:
#include <variant>
#include <iostream>
int main()
{
std::variant<long int, bool> v; // works fine if "long" is omitted
long int sanity = 1; // verify that we can assign 1 to a long int; works fine
std::cout << sizeof(sanity) << "\n";
v = 1; // compiler error here: why doesn't this assign to the long int variant of v?
return 0;
}
Error message:
error: no match for ‘operator=’ (operand types are ‘std::variant<long int, bool>’ and ‘int’)
Is there any magic to making this work as intended, without requiring explicit casts in the assignment? Thanks!
Assigning to a variant does not simply assign to the currently active type in the variant. Instead, the type of the right hand side is used to determine which of the possible types (the alternatives) is the best match for the right hand side. Then, that type is assigned to.
Thus,
v = 1;does not automatically assign to thelong intvalue that is already insidev, but rather, a compile-time computation is done in order to determine whetherlong intorboolis a better match forint(the type of the right hand side). The best match is determined using the overload resolution rules. In other words, we must imagine that two functions existand ask ourselves which function would be called by
f(1). If thelong intoverload is selected, thenv = 1assigns to thelong intobject. If thebooloverload is selected, then thelong intthat is currently insidevis destroyed and a newboolobject is constructed with the value 1.Unfortunately, this overload resolution is ambiguous:
1requires an "integral conversion" to match eitherlong intorbool. Thus, assigning 1 tovis a compile-time error. If the alternatives had beenintandbool, then theintalternative would yield an exact match and there would be no ambiguity.This particular example is fixed in C++20: the
boolalternative is removed from consideration since a narrowing conversion would be required to initialize aboolvalue from anintvalue. Thus, in C++20, this code will always assign to thelong intalternative (if there is aboolobject currently inside the variant, it is destroyed).