Продвижение указателя перегруженной функции C ++ на объект функции - PullRequest
3 голосов
/ 23 марта 2012

Шаблонная функция C ++ 11, которая принимает аргумент объекта функции, такой как:

template <typename F, typename T>
auto foo(F f, T x) -> decltype(f(x)) {
  return f(x);
}

может принимать такие функции, как:

int succ(int i) { return i+1; }

и применить foo к succ и получить результат 10:

foo(succ,9);

Перегруженные функции не работают, sin, например, не работает:

foo(std::sin,0.5);

в GCC (4.7) с «невозможно вывести параметр шаблона F».

(Предоставление sin<double> относится только к сложным типам между прочим.) Да, я могу структурировать это прямо:

template <typename T>
struct Sin {
  T operator()(T x) { return std::sin(x); }
};

с небольшим:

foo(Sin<double>(),0.5);

У меня вопрос: есть ли альтернатива, которая устраняет необходимость в таком новом определении; можно использовать только на сайте вызова foo?

1 Ответ

8 голосов
/ 23 марта 2012

Для указателей на функции вы можете просто указать, как пользователь вводит подпись:

template<class F, class T>
void foo(F f, T x){
  f(x);
}

void bar(int){}
void bar(double){}

int main(){
  foo<void(int)>(bar, 5);
}

Живой пример на Ideone .

foo будет void foo(void f(int), int x) после замены, что совпадает с foo(void (*f)(int), int x). Это обеспечивает так называемый «контекст вызова», который позволяет компилятору выбрать правильную перегрузку. Очевидно, это работает хорошо только в том случае, если первый параметр шаблона является функцией. Чтобы обойти это ограничение и сделать его лучше (imho, atleast), вы можете предоставить простую вспомогательную функцию:

template<class F>
auto get_overload(F f) -> decltype(f) { return f; }

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

Поскольку вы, скорее всего (или наверняка), хотите это только для указателей на функции, вы можете изменить его на следующее:

template<class F>
F* get_overload(F* f){ return f; }

Это все тот же. Единственная причина, по которой первая версия не может просто иметь F в качестве возвращаемого типа, заключается в том, что F равен void(int), если вы вызываете ее с помощью get_overload<void(int)>(bar), и стандарт не позволяет вам возвращать функции (да это тип функции). Функция преобразования указателя в функцию (void(int) -> void(*)(int)) выполняется только для параметров.


Поскольку по какой-либо причине @VJovic удалил свой ответ, я просто отредактирую его в:

Вы можете использовать простую лямбду вместо функции get_overload. Это будет примерно такой же длины символов, гораздо удобнее и понятнее. Это также будет более эффективным, так как не задействованы никакие (функциональные) указатели, и компилятор вполне может встроить вызов.

foo([](int i){ return bar(i); }, 5);
...