Какой способ вызова lambdas в С++ является самым идиоматическим?
Я заметил, что lambdas работают с указателями функций, а также с выделенным типом function
, используя g++
.
#include <functional>
typedef int(*fptr)(int, int);
int apply_p(fptr f, int a, int b) {
return f(a, b);
}
int apply_f(std::function<int(int, int)> f, int a, int b) {
return f(a, b);
}
void example() {
auto add = [](int a, int b) -> int {
return a + b;
};
apply_p(add, 2, 3); // doesn't give an error like I'd expect it to
apply_f(add, 2, 3);
}
Мой вопрос: какие из них наиболее идиоматичны? И каковы опасности и/или преимущества использования одного над другим?
Ответы
Ответ 1
Я заметил, что lambdas работают с использованием указателей функций, а также выделенного типа function
Если лямбда ничего не захватывает, тогда она может распадаться на указатель функции.
Какие из них наиболее идиоматичны?
Ни. Используйте function
, если вы хотите сохранить произвольный вызываемый объект. Если вы просто хотите создать и использовать его, сохраните его общий:
template <typename Function>
int apply(Function && f, int a, int b) {
return f(a,b);
}
Вы можете пойти дальше и задать тип и тип возвращаемого и аргументального; Я оставлю это как упражнение.
И каковы опасности и/или преимущества использования одного над другим?
Версия указателя функций работает только с (не-членными или статическими) функциями и не захватывает lambdas и не позволяет передавать какое-либо состояние; только сама функция и ее аргументы. Тип function
может обернуть любой вызываемый тип объекта, с состоянием или без него, поэтому более полезен. Тем не менее, у этого есть затраты времени выполнения: чтобы скрыть обернутый тип, вызов его будет включать вызов виртуальной функции (или аналогичный); и может потребоваться динамическое выделение памяти для хранения большого типа.
Ответ 2
Я бы сказал (С++ 14)
template <class Functor, class... Args>
decltype(auto) apply_functor(Functor&& f, Args&&... args) {
return std::forward<Functor>(f)(std::forward<Args>(args)...);
}
или С++ 11:
template <class Functor, class... Args>
auto apply_functor(Functor&& f, Args&&... args) ->decltype(std::forward<Functor>(f)(std::forward<Args>(args)...)) {
return std::forward<Functor>(f)(std::forward<Args>(args)...);
}
Ответ 3
Если есть сомнения, предпочитайте std::function
:
-
Синтаксис гораздо более прост и согласуется с другими типами, например,
typedef std::function<int (int, int)> MyAddFunction;
и
typedef int (*MyAddFunction)( int, int );
-
Это более общий (на самом деле может принимать лямбда или простая функция C или функтор)
-
Это более безопасный тип
apply_p(0, 0, 0); // probably crashes at runtime, dereferences null pointer
apply_f(0, 0, 0); // doesn't even compile