I have a class that mainly wraps a std::variant with some minor additional functionality/meta-data.
For simplicity of use, I wanted to provide a user-defined conversion of this wrapper class to the underlying variant type, so functions like std::holds_alternative could be called directly on it.
What I've uncovered leaves me very confused about whether and when user-defined conversions will be applied. Here is simplified code.
#include <iostream>
#include <variant>
struct MyClass
{
// "MyClass" is implicitly convertible to a variant<bool,int>
operator std::variant <bool, int> ()
{
return std::variant<bool,int>(5);
}
};
void thisFunctionTakesOneSpecificVariantType (std::variant<bool,int> v)
{
std::cout << v.index();
}
template <class... types>
void thisFunctionTakesAnyVariantType (std::variant<types...> v)
{
std::cout << v.index();
}
int main ()
{
MyClass mc;
// 1. This compiles and runs as expected,
// proving the user-defined conversion (MyClass -> variant<int,bool>) exists and works "sometimes"
thisFunctionTakesOneSpecificVariantType (mc);
// 2. This compiles and runs as expected,
// proving "thisFunctionTakesAnyVariantType" is well defined
thisFunctionTakesAnyVariantType (std::variant <bool, int> (5));
// 3. But, combining 1 & 2, this fails to compile:
/* fails */ thisFunctionTakesAnyVariantType (mc); // error: no matching function for call to 'thisFunctionTakesAnyVariantType'
// 4. This is what i really want to do, and it also fails to compile
/* fails */ std::holds_alternative<int>(mc); // error: no matching function for call to 'holds_alternative'
// 5. An explicit conversion works for 3 and 4, but why not an implicit conversion?
// After all, the implicit conversion worked in #1
thisFunctionTakesAnyVariantType ( static_cast<std::variant <bool, int>> (mc) );
return EXIT_SUCCESS;
}
Why don't use cases 3 and 4 compile, while 1, 2, and 5 do?
In the error messages, it provides this note:
note: candidate template ignored: could not match 'variant<type-parameter-0-1...>' against 'MyClass'
inline constexpr bool holds_alternative(const variant<_Types...>& __v)