'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?

https://godbolt.org/z/zv1o4nxne



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...>;

Demo

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*/)
    {/*..*/};

Demo

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