I have a class called foo_t
that has a member called bar
which could be any one of the types std::string
, int
, std::vector<double>
, etc. I would like to be able to ask foo_t
which type bar
has been assigned to. I decided to use std::variant
.
I've written a solution, but I'm not sure if this is a good use of std::variant. I'm not sure if it matters, but I expect the list of types to possibly grow much bigger in the future. I made an enum class to store which type std::variant is assigned to. My first implementation also available on wandbox:
#include <iostream>
#include <variant>
#include <vector>
#include <string>
enum foo_kind_t {
double_list,
name_tag,
number,
unknown
};
template <typename val_t>
struct get_foo_kind_t {
constexpr static foo_kind_t value = unknown;
};
template <>
struct get_foo_kind_t<int> {
constexpr static foo_kind_t value = number;
};
template <>
struct get_foo_kind_t<std::string> {
constexpr static foo_kind_t value = name_tag;
};
template <>
struct get_foo_kind_t<std::vector<double>> {
constexpr static foo_kind_t value = double_list;
};
class foo_t {
public:
foo_t(): kind(unknown) {}
template <typename val_t>
void assign_bar(const val_t &val) {
static_assert(get_foo_kind_t<val_t>::value != unknown, "unsupported assignment");
kind = get_foo_kind_t<val_t>::value;
bar = val;
}
foo_kind_t get_kind() {
return kind;
}
template <typename val_t>
val_t get_bar() {
if (get_foo_kind_t<val_t>::value != kind) {
throw std::runtime_error("wrong kind");
}
return std::get<val_t>(bar);
}
private:
foo_kind_t kind;
std::variant<
int,
std::string,
std::vector<double>
> bar;
};
template <typename val_t>
void print_foo(foo_t &foo) {
std::cout << "kind: " << foo.get_kind() << std::endl;
std::cout << "value: " << foo.get_bar<val_t>() << std::endl << std::endl;
}
int main(int, char*[]) {
// double_list
foo_t b;
std::vector<double> b_val({ 1.0, 1.1, 1.2 });
b.assign_bar(b_val);
std::cout << "kind: " << b.get_kind() << std::endl;
std::cout << "value: vector size: " << b.get_bar<std::vector<double>>().size() << std::endl << std::endl;
// name_tag
foo_t d;
std::string d_val("name");
d.assign_bar(d_val);
print_foo<std::string>(d);
// number
foo_t c;
int c_val = 99;
c.assign_bar(c_val);
print_foo<int>(c);
// unknown
foo_t a;
std::cout << a.get_kind() << std::endl;
return 0;
}
Is this a good way to do it? Is there a way having better performance? Is there a way that requires less code to be written? Is there a way that doesn't require C++17?
No, very explicitly. From [variant. variant]: Any instance of variant at any given time either holds a value of one of its alternative types, or it holds no value.
Since this function is specific to a given type, you don't need RTTI to perform the operations required by std::any .
std::variant (C++17)An instance of std::variant has a value from one of its types. The value must not be a reference, C-array or void. A std::variant can have one type more than once. A default-initialized std::variant will be initialized with its first type.
std::monostate is a value-semantic type, like bool — it just has one fewer value in its domain. It can be stored in variants, or containers, or set s (it's ordered!), or unordered_set s (it's hashable!), or anywhere else you might use a value-semantic type like bool . It's totally fine to make an optional<monostate> .
Using std::variant::index to check stored type at runtime.
If you only need to ask "Is this variant of type X" for a single type X, then I recommend that you prefer std::holds_alternative over std::variant::index because the line of code will not have to be updated if the index of the type in the variant changes in the future.
Example:
if (std::holds_alternative<X>(my_variant)) {
std::cout << "Variant is of type X" << std::endl;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With