В С++ 17 тривиально реализовать функцию overload(fs...), которая при любом количестве аргументов fs..., удовлетворяющих FunctionObject возвращает новый объект функции, который ведет себя как перегрузка fs.... Пример:
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"
}
Так как вышеприведенный overloader наследует от Ts..., ему нужно либо скопировать, либо переместить объекты функции для работы. Я хочу что-то, что обеспечивает одно и то же поведение перегрузки, но только ссылки на объекты переданной функции.
Позвоните в эту гипотетическую функцию ref_overload(fs...). Моя попытка заключалась в использовании std::reference_wrapper и std::ref следующим образом:
template <typename... Ts>
auto ref_overload(Ts&... xs)
{
return overloader<reference_wrapper<Ts>...>{ref(xs)...};
}
Кажется, достаточно просто, не так ли?
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
^
Причина, по которой он не работает, прост: std::reference_wrapper::operator() - это шаблон вариационной функции, который не играет хорошо с перегрузка.
Чтобы использовать синтаксис using Ts::operator()..., мне нужно Ts... выполнить FunctionObject. Если я попытаюсь создать свою собственную упаковку FunctionObject, я столкнусь с той же проблемой:
template <typename TF>
struct function_ref
{
TF& _f;
decltype(auto) operator()(/* ??? */);
};
Поскольку нет способа выражения "компилятора", заполните ??? теми же аргументами, что и TF::operator() ", мне нужно использовать шаблон вариационной функции, не решая ничего.
Я также не могу использовать что-то вроде boost::function_traits, потому что одна из функций, переданных в overload(...), может быть шаблоном функции или перегруженной функцией объект сам!
Поэтому мой вопрос: есть способ реализации функции ref_overload(fs...), которая при любом числе объектов функции fs... возвращает новый объект функции, который ведет себя как перегрузка fs..., но означает fs... вместо копирования/перемещения?