I would like to place a std::variant
inside a class and return its elements with a template function. Here is an example:
#include <string>
#include <variant>
class Class {
public:
std::variant<std::string, double> cont;
Class() {}
template <class V> Class(const V v) { cont = v; }
template <typename V> V fun() {
if (std::holds_alternative<double>(cont))
return std::get<double>(cont);
else if (std::holds_alternative<std::string>(cont))
return std::get<std::string>(cont);
}
};
int main() {
Class c;
c = 20;
double d = c.fun<double>();
return 0;
}
I try to return the elements of the class Class
through the template function fun
. However, gcc-9.1
refuses to compile the code and tells me
Class.cpp:12:46: error: cannot convert ‘std::__cxx11::basic_string<char>’ to ‘double’ in return
12 | return std::get<std::string>(cont);
Why is there any attempt to convert the string
(the second return type of the function foo
) to a double
? Can I prevent this and solve the problem? Do I use the std::variant
class unidiomatic?
The issue here is that you query the current value stored at runtime, while the function signature of the template instantiation is performed at compile time. Consider how the member function looks like when you try using it to retrieve a double
:
double fun() {
if (/* ... */)
return std::get<double>(cont); // Ok return type is double, too
else if (/* ... */)
return std::get<std::string>(cont); // Error, return type should be string?!
}
This can't work. You need to change the way to access the data member, e.g. passing an overload set to std::visit
, by providing two getter-like functions returning std::optional<double>
and std::optional<std::string>
or something similar.