Я хотел бы создать шаблон, используя тип, а также передать функцию, которая имеет аргумент шаблона, который содержит этот тип.
Вот что у меня сейчас есть:
#include <iostream>
void fooRef(int& ref) { std::cout << "In ref" << std::endl; }
void fooPtr(int* ptr) { std::cout << "In ptr" << std::endl; }
template<typename T, typename F>
void Print(T arg, F func) {
//DoABunchOfStuff();
func(arg);
//DoSomeMoreStuff();
}
int main() {
int x = 5;
int& ref = x;
Print<int*, void(*)(int*)>(&x, &fooPtr);
Print<int&, void(*)(int&)>(ref, &fooRef);
}
Это работает, но я чувствую, что может быть какое-то дополнительное многословие к вызывающей функции. В идеале я хочу, чтобы звонок выглядел примерно так:
Print<int*, fooPtr>(ptr);
Print<int&, fooRef>(ref);
Есть ли способ упростить вызовы функции печати?
Есть ли способ упростить вызовы функции печати?
Да. То, что вы делаете, не указываете типы шаблонов вообще. Шаблоны функций проходят через вызов процесса вычет аргумента шаблона. В этом процессе для параметров, передаваемых в функцию, выводится их тип, и компилятор пытается сопоставить его с параметрами шаблона. если это работает, то функция удаляется, и компилятор продолжает работу. Так что для вас код, если мы использовали
int main() {
int x = 5;
int& ref = x;
Print(&x, &fooPtr);
Print(std::ref(ref), &fooRef);
}
Тогда мы получим
In ptr
In ref
Во втором звонке я использовал std::ref
так что на самом деле пройдет ref
по ссылке. В противном случае это сделало бы копию любого ref
относится к.
Вы можете упростить это, используя вывод аргументов шаблона, смешанный с шаблонами с переменным числом аргументов. Вот как это можно сделать:
template<typename FunctionType, typename... ArgumentTypes>
void Print(FunctionType function, ArgumentTypes... args) {
function(args...);
}
Однако, если вы хотите правильно поддерживать ссылки, вам придется прибегнуть к пересылка ссылок, что позволяет компилятору определять не только тип отправляемого аргумента, но и тип значения.
template<typename F, typename... Args>
void Print(F function, Args&&... args) {
function(std::forward<Args>(args)...);
}
Теперь все выглядит нормально, но вам все равно приходится иметь дело с вашей функцией, принимающей любые аргументы. Ограничение вашей функции на прием только тех аргументов, которые сделают вызов действительным, может быть выполнено с помощью завершающего типа возврата:
// instead of specifying the return type before the function name,
// let's place it at the end and use `decltype` to deduce the right type:
template<typename F, typename... Args>
auto Print(F function, Args&&... args) -> decltype(function(std::declval<Args>()...)) {
return function(std::forward<Args>(args)...);
}
С этим кодом у вас будут более понятные ошибки от вашего компилятора:
void func1(int) {}
Print(func1, 1); // works!
Print(func1, 1, 7); // error: no such function to call
Если вы все еще хотите void
как ваш тип возврата и все еще ограничивают вашу функцию, вы можете положиться на void_t
для этого:
// void_t definition
template<typename...>
using void_t = void;
template<typename F, typename... Args>
auto Print(F function, Args&&... args) -> void_t<decltype(function(std::declval<Args>()...))> {
function(std::forward<Args>(args)...);
}
Да, ваш идеальный синтаксис прост:
void fooRef(int& ref) { (void)ref; std::cout << "In ref" << std::endl; }
void fooPtr(int* ptr) { (void)ptr; std::cout << "In ptr" << std::endl; }
template<class T, void(*func)(T)>
void Print(T arg) {
//DoABunchOfStuff();
func(std::forward<Arg>(arg));
//DoSomeMoreStuff();
}
int main() {
int x = 5;
int& ref = x;
Print<int*, fooPtr>(&x);
Print<int&, fooRef>(ref);
}
(void)ref
а также (void)ptr
добавлено подавление неиспользуемых переменных предупреждений. std::forward<T>
используется, чтобы сделать некоторые угловые случаи более эффективными. Я бы также посоветовал заблокировать вычет на T
с:
template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;
template<class T, void(*func)(T)>
void Print(block_deduction<T> arg) {
что заставит абонентов пройти T
явно, потому что я думаю, что T
здесь делает код немного хрупким (он должен точно соответствовать сигнатуре func
).