Search code examples
c++templatesc++17variadic-templates

Multiple std::variant visit with variadic templated helper


I am attempting to make a function that helps handling N std::variant types.

Note: I am trying to make all pathways compile time validated. So std::optional and std::holds_alternative are not viable for me.

The implementation is as follows:

template<typename T>
using Possible = std::variant<std::monostate, T>;

template<typename... Types>
void ifAll(std::function<void(Types...)> all, Possible<Types>&&... possibles)
{
    std::visit(
        [&](auto&&... args) {
            if constexpr ((... &&
                           std::is_same_v<std::decay_t<decltype(args)>, Types>))
            {
                return all(std::forward<Types>(args)...);
            }
            else
            {
                std::cout << "At least one type is monostate" << std::endl;
            }
        },
        possibles...);
}

And an example of using the function is:

int main()
{
    Possible<int>  a = 16;
    Possible<bool> b = true;

    ifAll([](const int& x, const bool& y)
              -> void { std::cout << "All types set!" << std::endl; },
          a,
          b);
}

However I get a compiler error:

TestFile.cc: error: no matching function for call to 'ifAll'
    ifAll([](const int& x, const bool& y)
    ^~~~~

TestFile.cc: note: candidate template ignored: could not match
    'function<void (type-parameter-0-0...)>' against '(lambda at
    TestFile.cc)'

void ifAll(std::function<void(Types...)> all, Possible<Types>&&... possibles)
    ^

Why does the lambda I provide not match the function signature?

Attempted Fix 1

I tried moving in a and b which still does not work:

ifAll([](const int& x, const bool& y)
              -> void { std::cout << "All types set!" << std::endl; },
          std::move(a),
          std::move(b));

Solution

  • Following call would work:

    int main() {
        Possible<int>  a = 16;
        Possible<bool> b = true;
    
        std::function<void(int, bool)> fun = [](int x, bool y) -> void {
            std::cout << "All types set!" << std::endl;
        };
    
        ifAll(fun,
              std::move(a),
              std::move(b));
    }
    

    or switch your function signature to:

    template <typename... Types>
    void ifAll(std::function<void(Types...)> const& all, Possible<Types>&... possibles)
    

    and then you can call it without std::move:

    int main() {
        Possible<int>  a = 16;
        Possible<bool> b = true;
    
        std::function<void(int, bool)> fun = [](int x, bool y) -> void {
            std::cout << "All types set!" << std::endl;
        };
    
        ifAll(fun, a, b);
    }