Есть ли простой способ? Нет. Можно ли это сделать, используя нечестивый беспорядок искаженных шаблонов? Конечно, почему бы и нет.
Осуществление
Во-первых, это будет немного проще, если у нас есть класс, а не функция, просто потому, что параметризованные классы могут быть переданы в качестве параметров шаблона. Итак, я собираюсь написать простую обертку вокруг вашего myFunc
.
template <bool... Acc>
struct MyFuncWrapper {
template <typename T>
void operator()(T&& extra) const {
return myFunc<Acc...>(std::forward<T&&>(extra));
}
};
Это просто класс, для которого MyFuncWrapper<...>()(extra)
эквивалентно myFunc<...>(extra)
.
Теперь давайте создадим нашего диспетчера.
template <template <bool...> class Func, typename Args, bool... Acc>
struct Dispatcher {
auto dispatch(Args&& args) const {
return Func<Acc...>()(std::forward<Args&&>(args));
}
template <typename... Bools>
auto dispatch(Args&& args, bool head, Bools... tail) const {
return head ?
Dispatcher<Func, Args, Acc..., true >().dispatch(std::forward<Args&&>(args), tail...) :
Dispatcher<Func, Args, Acc..., false>().dispatch(std::forward<Args&&>(args), tail...);
}
};
Уфф, тут есть что объяснить. Класс Dispatcher
имеет два аргумента шаблона, а затем список переменных. Первые два аргумента просты: функция, которую мы хотим вызвать (как класс), и «дополнительный» тип аргумента. Аргумент variadic будет начинаться с нуля, и мы будем использовать его в качестве аккумулятора во время рекурсии (аналогично аккумулятору, когда вы выполняете оптимизацию хвостового вызова ), чтобы накапливать логический список шаблона.
dispatch
- это просто рекурсивная функция шаблона. Базовый случай - это когда у нас не осталось никаких аргументов, поэтому мы просто вызываем функцию с аргументами, которые мы накопили до сих пор. В рекурсивном случае используется условие, в котором мы накапливаем true
, если логическое значение равно true
, и false
, если оно равно false
.
.
Мы можем назвать это с
Dispatcher<MyFuncWrapper, TypeOfExtraArgument>()
.dispatch(extraArgument, true, true, false);
Однако это немного многословно, поэтому мы можем написать макрос, чтобы сделать его более доступным. 1
#define DISPATCH(F, A, ...) Dispatcher<F, decltype(A)>().dispatch(A, __VA_ARGS__);
Теперь наш звонок
DISPATCH(MyFuncWrapper, extraArgument, true, true, false);
Полный исполняемый пример
Включает пример реализации myFunc
.
#include <utility>
#include <iostream>
#define DISPATCH(F, A, ...) Dispatcher<F, decltype(A)>().dispatch(A, __VA_ARGS__);
template <bool a, bool b, bool c, typename T>
void myFunc(T&& extra) {
std::cout << a << " " << b << " " << c << " " << extra << std::endl;
}
template <bool... Acc>
struct MyFuncWrapper {
template <typename T>
void operator()(T&& extra) const {
return myFunc<Acc...>(std::forward<T&&>(extra));
}
};
template <template <bool...> class Func, typename Args, bool... Acc>
struct Dispatcher {
auto dispatch(Args&& args) const {
return Func<Acc...>()(std::forward<Args&&>(args));
}
template <typename... Bools>
auto dispatch(Args&& args, bool head, Bools... tail) const {
return head ?
Dispatcher<Func, Args, Acc..., true >().dispatch(std::forward<Args&&>(args), tail...) :
Dispatcher<Func, Args, Acc..., false>().dispatch(std::forward<Args&&>(args), tail...);
}
};
int main() {
DISPATCH(MyFuncWrapper, 17, true, true, false);
DISPATCH(MyFuncWrapper, 22, true, false, true);
DISPATCH(MyFuncWrapper, -9, false, false, false);
}
Заключительные записки
Реализация, представленная выше, также позволит myFunc
возвращать значения, хотя в вашем примере был только тип возврата void
, поэтому я не уверен, что вам это понадобится. Как написано, реализация требует C ++ 14 для auto
возвращаемых типов. Если вы хотите сделать это в C ++ 11, вы можете либо изменить все возвращаемые типы на void
(больше не может ничего возвращать из myFunc
), либо вы можете попытаться объединить возвращаемые типы с помощью decltype
, Если вы хотите сделать это в C ++ 98, ... ... ... ... удачи
1 Этот макрос подвержен проблеме с запятыми и, следовательно, не будет работать, если вы передадите ему нулевое логическое значение. Но если вы не собираетесь передавать какие-либо логические значения, вам, вероятно, не стоит проходить этот процесс в любом случае.