Как я могу передать функцию в шаблонную функцию A, которая может иметь другую сигнатуру на основе другого параметра для A?

Я хотел бы создать шаблон, используя тип, а также передать функцию, которая имеет аргумент шаблона, который содержит этот тип.

Вот что у меня сейчас есть:

#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);

Есть ли способ упростить вызовы функции печати?

3

Решение

Есть ли способ упростить вызовы функции печати?

Да. То, что вы делаете, не указываете типы шаблонов вообще. Шаблоны функций проходят через вызов процесса вычет аргумента шаблона. В этом процессе для параметров, передаваемых в функцию, выводится их тип, и компилятор пытается сопоставить его с параметрами шаблона. если это работает, то функция удаляется, и компилятор продолжает работу. Так что для вас код, если мы использовали

int main() {
int x = 5;
int& ref = x;

Print(&x, &fooPtr);
Print(std::ref(ref), &fooRef);
}

Тогда мы получим

In ptr
In ref

Живой пример

Во втором звонке я использовал std::ref так что на самом деле пройдет ref по ссылке. В противном случае это сделало бы копию любого ref относится к.

3

Другие решения

Вы можете упростить это, используя вывод аргументов шаблона, смешанный с шаблонами с переменным числом аргументов. Вот как это можно сделать:

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)...);
}
1

Да, ваш идеальный синтаксис прост:

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).

1
По вопросам рекламы [email protected]