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));
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);
}
An easy solution is to use a function object + std::optional
:
#include <functional>
#include <optional>
struct Error {};
template <typename F, typename... Args>
decltype(auto) if_all(F&& f, Args&&... args)
{
if ((args && ...)) {
return std::invoke(std::forward<F>(f), *std::forward<Args>(args)...);
} else {
throw Error{};
}
}
Usage example:
#include <functional>
#include <iostream>
int main()
{
std::optional<int> a{5};
std::optional<int> b{10};
std::cout << if_all(std::plus{}, a, b) << '\n';
}
(live demo)
If you insist to use std::variant
instead of std::optional
(which is probably because of some misunderstandings about either of them), the idea is the same — you need to check if all arguments are "empty" first (maybe using std::holds_alternative
), and unwrap the arguments after.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With