I am trying to wrap my head around std::variant and std::visit and I am trying to come up with a method to specify a couple of types that I would like my variable to hold (which would go into my std::variant) and then retrieve stored data through a std::visit. Consider the following example:
#include <iostream>
#include <variant>
#include <string>
struct PrintType {
void operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
}
void operator()(const double &data) {
std::cout << "visiting double node" << std::endl;
}
};
struct SingleOperatorOverload {
int operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
};
struct AllTypesOperatorOverload {
int operator()(const int &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
double operator()(const double &data) {
std::cout << "visiting double node" << std::endl;
return data;
}
};
int main() {
using var_t = std::variant<int, double>;
// print int related operator() content, OK
var_t foo = 42;
std::visit(PrintType(), foo);
// print double related operator() content, OK
foo = 3.1415;
std::visit(PrintType(), foo);
// get value and store into bar, struct with single operator(), OK
foo = 42;
auto bar = std::visit(SingleOperatorOverload(), foo);
std::cout << "bar: " << bar << std::endl;
// get value and store into bar, struct with multiple operator(), ERROR
auto bar = std::visit(AllTypesOperatorOverload(), foo);
std::cout << "bar: " << bar << std::endl;
return 0;
}
The variant is allowed to hold (in this simplified example) either int or double. If I just want to print something based on the type (as done with the PrintType struct), that works fine.
If I want to retrieve data through a visitor as done in the SingleOperatorOverload class, which only provides an implementation for the operator() accepting an int as a parameter, that works. However, as soon as I try to implement an operator() for each type in the std::variant, i.e. here int and double, as in the AllTypesOperatorOverload struct, I get a compilation error error: invalid conversion from '...' {aka double ...} to '...' {aka int ...} so it seems std::variant is handling function signatures differently?
I tried SFINAE but that does not seem to alleviate the problem
struct AllTypesOperatorOverload {
template<typename T, std::enable_if_t<std::is_same<T, int>::value>>
T operator()(const T &data) {
std::cout << "visiting int node" << std::endl;
return data;
}
template<typename T, std::enable_if_t<std::is_same<T, double>::value>>
T operator()(const T &data) {
std::cout << "visiting double node" << std::endl;
return data;
}
};
This will now report an error: no type named 'type' in 'struct std::invoke_result<AllTypesOperatorOverload, int&>'. Is there a way to provide operator() for all types and then receive their respective values into bar with the correct type depending on how foo has been set? I am aware of std::get_if<T>() which may be of use here, but ideally, I don't want to have a long if statement checking for each type, unless absolutely necessary (this is a simplified example, I may want to have several more types in my std::variant).