Is there a clean way to turn an overload set into a visitor suitable for use with std::visit?
Asked Answered
S

3

3

Suppose I have an overload set, like so:

class C
{
  public: 

    static void f(const A &);
    static void f(const B &);
};

I'd like to do something like

std::variant<A, B> v;

// ...

std::visit(C::f, v);

but this doesn't compile. Is there some way of taking an overload set and regarding it as, or converting it to, a Visitor?

Synchroscope answered 26/2, 2021 at 19:25 Comment(0)
S
5

Actually, after playing around with this further I've realized that I can do

std::visit([](const auto & t) { C::f(t); }, v);

so I'll leave this here as a potential solution for anyone else with the same problem.

Synchroscope answered 26/2, 2021 at 19:32 Comment(7)
You could improve it with perfect forwarding: [](auto&& arg){ C::f(std::forward<decltype(arg)>(arg));}. Other than that I believe this is the cleanest solution there is because overload sets simply cannot be passed around.Gomphosis
@Gomphosis in general I'd agree that's an improvement, but this particular example set doesn't seem to warrant perfect forwarding, considering each overload only accepts a constant reference.Suzannasuzanne
@Gomphosis Could remove the forward and do decltype(arg)(arg).Unheard
@PatrickRoberts Yes, but as soon as an overload uses an rvalue ref, it will fail to compile.Gomphosis
@Unheard I don't think so? That would not forward it, right? You could do (decltype(args)&&)(arg) but that is exactly what forward does anyway.Gomphosis
@Gomphosis Let's assume it was T&& arg instead. decltype(arg) would give you T&&, not T, so decltype(arg)(arg) is enough.Unheard
@Unheard I see, you are right, thank you.Gomphosis
B
2

If you already use Boost, Boost.HOF has a utility to wrap an overload set for you: BOOST_HOF_LIFT:

std::visit(BOOST_HOF_LIFT(C::f), v);

Demo

Burney answered 26/2, 2021 at 19:54 Comment(0)
U
1

If you can make an instance of class C, you could overload the member operator() on class C to call the appropriate function based on the variant type:

#include <iostream>
#include <variant>

struct A {};
struct B {};

class C
{
    public:    
    template <typename T>
    void operator()(const T& t) { C::f(t); }

    static void f(const A &) { std::cout << "A\n"; }
    static void f(const B &) { std::cout << "B\n"; }
};

int main()
{
    std::variant<A, B> v_a = A{};
    std::variant<A, B> v_b = B{};
    C c;
    std::visit(c, v_a); // "A"
    std::visit(c, v_b); // "B"
    return 0;
}
Usn answered 26/2, 2021 at 19:50 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.