Reputation: 60381
I would like to design a class:
template <class... F> class overload_set;
that will take a list of callables at construction, and will have an operator()
that will apply the classical overload resolutions rules to decide which function to call.
For two functions, this would be something like this:
template <class F0, class F1>
struct overload_set: F0, F1
{
constexpr overload_set(F0 f0, F1 f1): F0(f0), F1(f1) {}
using F0::operator();
using F1::operator();
};
However, this would only work with function objects, while I would like it to work with any callable working with std::invoke
(free functions, functors, lambdas, functions members, members...). And I would like it of course, to work with all the subtelties of the ref/const qualification of function members like in:
struct subtle
{
constexpr void operator()() noexcept {}
constexpr void operator()() & noexcept {}
constexpr void operator()() && noexcept {}
constexpr void operator()() const& noexcept {}
constexpr void operator()() const&& noexcept {}
};
If subtle
is passed to overload_set
it should work well.
Is it possible to create such a class overload_set
in C++17, and if so how (with as much template metaprogramming tricks as required, but as little C macros as possible (hopefully none))?
Upvotes: 0
Views: 168
Reputation: 21160
Implementing what @KerrekSB said
template<typename F>
struct functor
{
using type = F;
};
template<bool Noexcept, typename R, typename... Args>
struct functor<R (*)(Args...) noexcept(Noexcept)>
{
struct fn
{
R (*p)(Args...) noexcept(Noexcept);
R operator()(Args&&... args) const noexcept(Noexcept)
{
return p(std::forward<Args>(args)...);
}
};
using type = fn;
};
template<typename F>
using func = typename functor<std::decay_t<F>>::type;
template<typename... Fs>
struct over : func<Fs>...
{
template<typename... Gs>
over(Gs&&... gs) : func<Fs>{std::forward<Gs>(gs)}... {}
using func<Fs>::operator()...;
};
template<typename... Gs>
auto makeover(Gs&&... gs)
{
return over<func<Gs>...>{std::forward<Gs>(gs)...};
}
You use it as
int main()
{
auto o = makeover([](int){ std::cout << "int\n"; },
+[](double){ std::cout << "double\n"; });
o(42); // prints int
o(42.0); // prints double
auto o2 = makeover(+[]() noexcept {});
std::cout << noexcept(o2()); // prints 1
}
Upvotes: 1