Функтор шаблона не может определить тип ссылки - PullRequest
4 голосов
/ 29 марта 2010

У меня есть функтор f, который принимает функцию func и параметр t того же типа, что и func. Я не могу передать g в f из-за ошибки компиляции (нет подходящей функции для вызова f(int&, void (&)(int&))). Если g примет нереферентный параметр g (int s), компиляция завершится. Или, если я вручную укажу параметр шаблона f<int&>(i, g), компиляция также завершится.

template<typename T>
void f(T t, void (*func)(T)) {}

void g(int& s) {}

int main(int, char*[])
{
    int i = 7;

    f(i, g); // compilation error here

    return 0;
}

Как я могу получить отчисления на работу?

Ответы [ 4 ]

6 голосов
/ 29 марта 2010

Вы можете вызвать функцию следующим образом:

f<int&>(i, g);

Но теперь я буду также передаваться по ссылке.

В общем, я бы тоже сделал функцию типом шаблона:

template <typename T, typename F>
void f(T t, F func) 
{ 
    func(t); //e.g
}
5 голосов
/ 30 марта 2010

Проблема в том, что если в шаблоне один из его параметров функции не является ссылочным типом до начала вывода, этот параметр никогда не будет выводиться в ссылочный тип. Таким образом, при вычете с левой стороны, T дает int, но при вычете с правой стороны, T дает int&. Это ошибка, и компилятор жалуется.

Лучше всего сделать параметр функции того же типа, что и тип параметра указателя функции:

template<typename T> struct identity { typedef T type; };

template<typename T>
void f(typename identity<T>::type t, void (*func)(T)) {}

Используя identity<T>::type, вы отключаете вычет на левой стороне. Как только T был определен с правой стороны, T устанавливается на левую сторону и выдает окончательный тип параметра.

Один парень предложил использовать правую сторону в качестве параметра шаблона - это хорошо, поскольку он может затем принимать объекты функций с перегруженной operator(). Но тогда вы сталкиваетесь с проблемой необходимости знать, хочет ли он ссылки или нет. Чтобы решить эту проблему, boost имеет reference_wrapper (кстати, boost также имеет шаблон identity выше).

template<typename T, typename F>
void f(T t, F func) {}

Теперь, если вы хотите передать ссылку, а не копию, вы можете сделать это следующим образом

int i;
f(boost::ref(i), some_function);

ref возвращает объект reference_wrapper, который неявно преобразуется в T&. Поэтому, если вы вызываете func(t), t автоматически преобразуется в целевую ссылку. Если вы не хотите передавать ссылку, просто передайте i напрямую.

5 голосов
/ 29 марта 2010

Я думаю, вам нужно либо:

void f(T t, void (*func)(T&)) {}

или

void g(int s) {}

но я предпочитаю:

template<typename T, typename T2> 
void f(T t, T2 func) {}

, так как это будет работать с функциями и функторами.

1 голос
/ 29 марта 2010
template<typename T>
void f(T t, void (*func)(T)) {}

Ключевым моментом здесь является то, что вы использовали T в обоих аргументах. Это означает, что типы должны точно соответствовать.

void g(int& s) {}

int i = 7;
f(i, g);

В вашем коде вы передаете int и функцию, принимающую от int& до f(). Это разных типов, но ваш шаблон для f ожидает два типа того же . Как и предлагали другие, самое простое решение - сделать функцию также шаблоном.

template <typename T, typename F>
void f(T t, F func) 
{ 
    func(t);
}
...