Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++17

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

like image 378
fen Avatar asked Aug 29 '18 10:08

fen


People also ask

What does visit() do c++?

std::visit from C++17 is a powerful utility that allows you to call a function over a currently active type in std::variant .

What is std variant?

std::variant variant is a value type that's used to represent a choice of types. The class takes a list of types, and the variant will be able to contain one value of any of those types. It is often referred to as tagged union, because similar to a union, it can store multiple types, with only one present at a time.


2 Answers

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)

like image 93
bolov Avatar answered Oct 06 '22 07:10

bolov


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...>;
like image 23
Evg Avatar answered Oct 06 '22 06:10

Evg