I try to understand std::variant in C++Builder Rad Studio Rio 10.3.1 (CLANG version 5.0). In a console app, I simply have:
#include <stdio.h>
#include <variant>
int _tmain(int argc, _TCHAR* argv[])
{
std::variant<int, float> v;
v = 12;
return 0;
}
When I try to compile, it returns an error:
I am confused because it is an example that I found in several web pages. It is expected to compile without problems. The 12 value should not be candidate for float. However, using the CLANG compiler of Rad Studio it does not compile.
This is a RAD Studio-specific problem.
This is a bug in the <variant>
header (still present as of version 11 update 2). std::variant
should have a converting constructor which chooses an alternative by the same logic as choosing and overload:
void foo(int){}
void foo(float){}
int _tmain(int argc, _TCHAR* argv[])
{
std::variant<int, float> v;
foo(12); //invokes foo(int)
v = 12; //thus std::variant<int, float>(12) should construct int alternative
return 0;
}
However, RAD header has an std::enable_if
checking if exactly one of the alternatives can be constructed from a given T
(via std::is_constructible
). And since both std::is_constructible_v<int, int>
and std::is_constructible_v<float, int>
are true, it doesn't enable any converting constructors, hence your problem:
int _tmain(int argc, _TCHAR* argv[])
{
std::variant<int, float> v(12); //error: no matching constructor
std::variant<int, std::string> v2(12); //ok
return 0;
}
How to patch this:
You can derive your own my::variant
from std::variant
(either a fully generic template class or a specialization you're using) and reimplement proper constructors yourself (by using std::in_place_type_t
to explicitly choose the alternative). Notice that this can get you in trouble with unintentional polymorphism.
You can include something like this as a quick fix (this is a very dirty hack and as written allows only the exact type match; one can improve this by implementing the proper template check):
namespace std {
template<typename T, typename... Args>
struct _One_t<is_constructible, T, Args...> : public _One_t<is_same, T, Args...>{
};
}