fen
fen

Reputation: 10125

What are the ways to skip some overload combinations of variant types in std::visit?

std::visit supports multiple input variants. The code, however, should handle all combinations of the types from those variants.

Is there a way to skip not "meaningful" combinations?

for example:

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

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

int main() {
    std::variant<int, float, char> v1 { 's' };
    std::variant<int, float, char> v2 { 10 };

    std::visit(overloaded{
        [](int a, int b) { },
        [](int a, float b) { },
        [](int a, char b) { },
        [](float a, int b) { },
        [](float a, float b) { },
        [](float a, char b) { },
        [](char a, int b) { },
        [](char a, float b) { },
        [](char a, char b) { }
    }, v1, v2);

    return 0;
}

is there a chance to implement only several important combinations, and "leave" the rest? (of course right now the compiler will report nasty error if you forget to implement one combination...)

maybe generic lambdas?

std::visit(overloaded{
        [](int a, int b) { },
        [](int a, float b) { },
        [](int a, char b) { },
        [](float a, int b) { },
        [](auto a, auto b) { }, // <<
    }, v1, v2);

That works, but I wonder if there's some better solution?

Update: Here's the playground with the solutions mentioned in the answers: http://coliru.stacked-crooked.com/a/78d9f2f25789bad2

Upvotes: 6

Views: 728

Answers (2)

bolov
bolov

Reputation: 75924

Yes, generic lambdas are a very good solution. Your proposed solution literally works ad litteram.

Usual overload rules apply.

[](auto a, auto b)

is equivalent in this sense with

template <class T1, class T2> auto foo(T1 a, T2 b) const;

Anything that doesn't match exactly one of the non-templated overloads will call the generic lambda.

You can mix things up a bit by providing something like [] (int a, auto b) and [] (auto a, auto b). Still usual overload rules apply.

Or mix things up even more with []<class T>(T a, T b) (since C++20)

Upvotes: 7

Evg
Evg

Reputation: 26372

Another option would be to change overloaded to something like this:

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

    template<class... Us>
    void operator()(const Us&...) const {  }
};

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

Upvotes: 3

Related Questions