Raildex
Raildex

Reputation: 4766

Call std::visit with passed lambdas

I have a struct that contains a variant.

I want to write a member function for that struct that should run code depending on which type variant currently holds.

However, I have issues making it compile. I don't want to use more "template shenanigans" like using a separate struct to define operator(T&) since it pollutes the syntax even more. Here is an example:

struct Data {

std::variant<int, double> var;


//Into this function,multiple lambdas should be passed for cases that the user wants to handle
template<typename ... Funcs>
void apply(Funcs&&... funcs) {
    std::visit(std::forward<Funcs>(funcs)...,var);
}
};
int main() {
    Data d;
    d.var = 4;
    //variant holds int and lambda provided that takes int&, execute it:
    d.apply([](int& i){
        std::cout << "I am Int Poggers" << std::endl;
    });
    d.var = 0.0;
    //variant holds double but no lambda passed that takes a double, hence nothing happens:
    d.apply([](int& i){
        std::cout << "I am Int Poggers" << std::endl;
    });
}

and I even don't know what the compiler wants from me: https://godbolt.org/z/oM4584anf

Upvotes: 2

Views: 2814

Answers (1)

max66
max66

Reputation: 66230

Your problem is that std::visit() needs a "visitor" that must handle every type of the std::variant.

However, I have issues making it compile. I don't want to use more "template shenanigans" like using a separate struct to define operator(T&) since it pollutes the syntax even more.

There is nothing complicated.

You can simply add a trivial struct (with deduction guide) as follows (and as proposed in the cppreference std::visit() page)

template<class... Ts> struct overloaded : Ts...
 { using Ts::operator()...; };

template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

Then, given that you want that your std::visit() return void, you can add, in your apply() method, a generic-do-nothing lambda

template<typename ... Funcs>
void apply(Funcs&&... funcs) {
    std::visit(overloaded{ // <-- pass through overloaded
               [](auto const &){}, // <-- and add this generic lambda
               std::forward<Funcs>(funcs)...},var);
}

Now the first apply() call

d.apply([](int& i){
    std::cout << "I am Int Poggers" << std::endl;
});

should compile calling the supplied lambda because is a better match (given that d contains an int) and the second call compile calling the do-nothing generic lambda, because the generic lambda is a better match for a double.

Upvotes: 9

Related Questions