В С++ 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...
вместо копирования/перемещения?