Функтор шаблона не может определить ссылочный тип

У меня есть функтор 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;
}

Как я могу получить вычет для работы?


person maciekp    schedule 29.03.2010    source источник
comment
Найдите время, чтобы научиться вводить блок кода (большой и встроенный). Только после того, как я отредактировал ваш вопрос, стало очевидно, что вы уже знаете о явном прохождении, о котором я упоминал в ответе.   -  person UncleBens    schedule 29.03.2010


Ответы (4)


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

f<int&>(i, g);

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

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

template <typename T, typename F>
void f(T t, F func) 
{ 
    func(t); //e.g
}
person UncleBens    schedule 29.03.2010

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

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

or:

void g(int s) {}

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

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

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

person Charles Beattie    schedule 29.03.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 напрямую.

person Johannes Schaub - litb    schedule 29.03.2010
comment
Хм, identity версия тоже ломается с const int i. - person Georg Fritzsche; 30.03.2010
comment
@gf не могли бы вы показать пример? Я не уверен, что вы имеете в виду и где ломается. - person Johannes Schaub - litb; 30.03.2010
comment
Извините - при использовании const int i в примере OP, i не может быть передан в результирующий f(int&,void(*)(int&)). - person Georg Fritzsche; 30.03.2010
comment
@gf, однако, я думаю, что это во многом предполагаемое поведение. Предположим, f примет его просто своим параметром. Но это не принесет никакой пользы, потому что мы все равно получим ошибку при вызове func(t). f хорошо отказывается от этого авансом, если я что-то не упускаю :) - person Johannes Schaub - litb; 30.03.2010
comment
Ага, да ... я собираюсь наверстать упущенное поспать. Извините за недоразумение :) - person Georg Fritzsche; 30.03.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);
}
person Michael Kristofik    schedule 29.03.2010