In C++17, it is trivial to implement an overload(fs...)
function that, given any number of arguments fs...
satisfying FunctionObject
, returns a new function object that behaves like an overload of fs...
. Example:
template <typename... Ts>
struct overloader : Ts...
{
template <typename... TArgs>
overloader(TArgs&&... xs) : Ts{forward<TArgs>(xs)}...
{
}
using Ts::operator()...;
};
template <typename... Ts>
auto overload(Ts&&... xs)
{
return overloader<decay_t<Ts>...>{forward<Ts>(xs)...};
}
int main()
{
auto o = overload([](char){ cout << "CHAR"; },
[](int) { cout << "INT"; });
o('a'); // prints "CHAR"
o(0); // prints "INT"
}
Since the above overloader
inherits from Ts...
, it needs to either copy or move the function objects in order to work. I want something that provides the same overloading behavior, but only references to the passed function objects.
Let's call that hypothetical function ref_overload(fs...)
. My attempt was using std::reference_wrapper
and std::ref
as follows:
template <typename... Ts>
auto ref_overload(Ts&... xs)
{
return overloader<reference_wrapper<Ts>...>{ref(xs)...};
}
Seems simple enough, right?
int main()
{
auto l0 = [](char){ cout << "CHAR"; };
auto l1 = [](int) { cout << "INT"; };
auto o = ref_overload(l0, l1);
o('a'); // BOOM
o(0);
}
error: call of '(overloader<...>) (char)' is ambiguous
o('a'); // BOOM
^
The reason it doesn't work is simple: std::reference_wrapper::operator()
is a variadic function template, which does not play nicely with overloading.
In order to use the using Ts::operator()...
syntax, I need Ts...
to satisfy FunctionObject
. If I try to make my own FunctionObject
wrapper, I encounter the same issue:
template <typename TF>
struct function_ref
{
TF& _f;
decltype(auto) operator()(/* ??? */);
};
Since there's no way of expressing "compiler, please fill the ???
with the exact same arguments as TF::operator()
", I need to use a variadic function template, solving nothing.
I also cannot use something like boost::function_traits
because one of the functions passed to overload(...)
may be a function template or an overloaded function object itself!
Therefore my question is: is there a way of implementing a ref_overload(fs...)
function that, given any number of fs...
function objects, returns a new function object that behaves like an overload of fs...
, but refers to fs...
instead of copying/moving them?
using
declaration to bring all of thoseoperator()
's into your own overload set. Thus allowing the C++ compiler to do the work of overload resolution for you. Once you can no longer use that trick, you're now stuck with manually implementing overload resolution. That will be... decidedly non-trivial. Good luck. – Dominiqueoverload
can have a singleoperator()
template, the you need to select which one of theTs...
matches. You can take inspiration from the implementation ofstd::variant
constructor, which basically needs to solve the same problem.Boost.Variant
implements it here. – Thistlyvariant
's straightforward, you set up a bunch offun(T)
for every type in the variant and run overload resolution withfun(arg)
to see which one is selected. OP's case is decidedly non-straightforward. – Fitztemplate<class T>struct not_T{operator T()&&;};
Covering all of the overload rules is probably impossible, however. – Agatewareboost::hana
. BTW, here's a possible workaround that abuses placementnew
to move back the overloaded functions into their original places: on wandbox – Maravediref_overload
implementation: on wandbox. – Maravedivoid
pointers toTs
instead of copying theTs
objects (erasing their type), while still keeping the original types using just the template context, so thevoid
pointers can then later bestatic_cast
back toTs
inside? – EllisonTs...
to haveusing Ts::operator()...
. – Maravedioperator.
overloading and really looking forward to it :) - I needref_overload
in C++17, though... – Maravedi