'Specialize a visitor for types under a namespace
I have a variant which can contain 8 different types, some of them under a particular namespace
std::variant<T0, T1, T2, ns::T0, ns::T1, ns::T2> v;
There exists a free function which can be called with any of these types and I am visiting the variant like this
int function(T0 t){return 0;}
int function(T1 t){return 1;}
int function(T2 t){return 2;}
namespace ns {
template <typename T>
int function(T t) {return 3;}
}
void doSomething() {
std::visit([](const auto &t) {return function(t);}, v);
}
However, now I want the user to be able to provide his own visitor and call that, and in case, the provided visitor doesn't match for the variant type, use the default one. To do so I have tried the following
int function(T0 t){return 0;}
int function(T1 t){return 1;}
int function(T2 t){return 2;}
namespace ns {
template <typename T>
int function(T t) {return 3;}
}
struct DefaultVisitor
{
template <typename T>
int operator()(T t) const {return function(t);}
}
template <typename VisitorT>
void doSomething() {
std::visit(overloaded{VisitorT(), DefaultVisitor()}, v);
}
If the VisitorT provides a call operator for only one type, this works fine, but if it provides a call operator for several types, such as a generic lambda, then it doesn't compile.
How could I provide a VisitorT which is used by the types in the ns namespace, but not for other types?
Solution 1:[1]
You might adapt overloaded to have some priorities:
template <typename T1, typename... Ts>
struct ranked_overloaded : T1, ranked_overloaded<Ts...>
{
template <typename... Us>
auto operator()(Us&&... args)
{
if constexpr (requires{ T1::operator()(std::forward<Us>(args)...); }) {
return T1::operator()(std::forward<Us>(args)...);
} else {
return ranked_overloaded<Ts...>::operator()(std::forward<Us>(args)...);
}
}
};
template <typename T1>
struct ranked_overloaded<T1> : T1
{
};
template <typename...Ts>
ranked_overloaded(Ts...) -> ranked_overloaded<Ts...>;
There is no ways to know for sure that type belongs to a specific namespace, but there is a trick which might be used to know if a type allow to found function via ADL (so types inheriting from ns type, or std::vector<ns::T0> would pass that check)
namespace ns
{
template <typename T>
void is_found_by_ADL_in_ns(T&&); // Declared only in that namespace
// No definition needed.
}
constexpr auto ns_lambda =
[](const auto &ns_obj)
-> decltype(is_found_by_ADL_in_ns(ns_obj), /*return type of the lambda*/)
{/*..*/};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
