The pattern that a lot of people use with C++17 / boost variants looks very similar to switch statements. For example: (snippet from cppreference.com)
std::varia
If you want to only allow a subset of types, then you can use a static_assert
at the beginning of the lambda, e.g.:
template
struct is_one_of:
std::disjunction, Args>...> {};
std::visit([](auto&& arg) {
static_assert(is_one_of{}, "Non matching type.");
using T = std::decay_t;
if constexpr (std::is_same_v)
std::cout << "int with value " << arg << '\n';
else if constexpr (std::is_same_v)
std::cout << "double with value " << arg << '\n';
else
std::cout << "default with value " << arg << '\n';
}, v);
This will fails if you add or change a type in the variant, or add one, because T
needs to be exactly one of the given types.
You can also play with your variant of std::visit
, e.g. with a "default" visitor like:
template
struct visit_only_for {
// delete templated call operator
template
std::enable_if_t{}> operator()(T&&) const = delete;
};
// then
std::visit(overloaded {
visit_only_for{}, // here
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}, v);
If you add a type that is not one of int
, long
, double
or std::string
, then the visit_only_for
call operator will be matching and you will have an ambiguous call (between this one and the default one).
This should also works without default because the visit_only_for
call operator will be match, but since it is deleted, you'll get a compile-time error.