Код, который я ищу, похож на следующий.
bool Func1(int Arg1, C++11LambdaFunc Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
}
Позже я буду использовать этот код.
Func1(12, [](int D) -> bool { ... } );
Код, который я ищу, похож на следующий.
bool Func1(int Arg1, C++11LambdaFunc Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
}
Позже я буду использовать этот код.
Func1(12, [](int D) -> bool { ... } );
Базовая версия для использования в файле заголовка:
template<typename Lambda>
bool Func1(int Arg1, Lambda Arg2){ // or Lambda&&, which is usually better
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
Более сложная версия, если вы хотите разделить свой интерфейс с вашей реализацией (у нее есть затраты времени на запуск):
bool Func1(int Arg1, std::function<bool(int)> Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
std::function
использует стирание типа для создания созданной пользователем обертки вокруг вашей лямбда, а затем предоставляет не виртуальный интерфейс, который использует шаблон pImpl
для пересылки его в созданный по умолчанию обертку. 1
Или, менее технически, std::function<bool(int)>
- это класс, который может обернуть почти все, что вы можете назвать как функция, передавая один параметр, совместимый с передачей int
, и возвращает то, что совместимо с возвращая a bool
.
Вызов через std::function
имеет стоимость времени выполнения, примерно равную вызову функции virtual
(вызванному стиранием стираемого типа), а при его создании он должен скопировать состояние объекта функции (aka функтор) (которые могут быть дешевыми - безъядерными лямбдами или лямбдами, захватывающими аргументы по ссылке - или дорогостоящими в некоторых других случаях) и хранить их (как правило, в свободном магазине или куче, которая имеет стоимость), тогда как чистая -template версии могут быть "inlined" в точке вызова (т.е. может стоить не только меньше, чем вызов функции, но и компилятор может оптимизировать вызов функции и вернуть границы!)
Причудливая версия первого примера, которая также лучше обрабатывает некоторые угловые случаи: (также должна быть реализована в файле заголовка или в том же блоке перевода, который используется)
template<typename Lambda>
bool Func1(int Arg1, Lambda&& Arg2){
if(Arg1 > 0){
return std::forward<Lambda>(Arg2)(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
который использует технику, известную как "совершенная пересылка". Для некоторых функторов это порождает несколько другое поведение, чем # 1 (и обычно более правильное поведение).
Большая часть усовершенствований приходит из использования &&
в списке аргументов: это означает, что ссылка на функтор передается (вместо копии), сохраняя некоторые затраты и позволяя как const
, так и не const
, который должен быть передан.
Изменение std::forward<Lambda>(...)
приведет только к изменению поведения, если кто-то использует относительно новую функцию С++, которая позволяет методам (включая operator()
) переопределять статус rvalue/lvalue указателя this
. Теоретически это может быть полезно, но число функторов, которые я видел, которые фактически переопределяют на основе состояния rvalue this
, равно 0
. Когда я пишу серьезный библиотечный код (tm), я иду на эту тему, но редко в противном случае.
Есть еще одна возможность рассмотреть. Предположим, вы хотите взять либо функцию, которая возвращает bool
, либо функцию, которая возвращает void
, и если функция возвращает void
, вы хотите обработать ее, как если бы она вернула true
. Например, вы выполняете функцию, которая вызывается при итерации по какой-либо коллекции, и вы хотите опционально поддерживать раннее завершение. Функция возвращает false
, когда она хочет остановиться преждевременно, и true
или void
в противном случае.
Или, в более общем случае, если у вас есть несколько переопределений функции, одна из которых принимает функцию, а другие принимают другой тип в одном месте.
Это возможно, насколько я собираюсь войти здесь (либо с помощью смарт-адаптера, либо с помощью методов SFINAE). Однако вам, вероятно, лучше просто создать две разные именованные функции, потому что требуемые методы слишком тяжелы.
1 Технически std::function
может использовать волшебную волшебную пыль, чтобы делать то, что она делает, поскольку ее поведение описывается стандартом, а не его реализацией. Я описываю простую реализацию, которая аппроксимирует поведение реализации std::function
, с которой я взаимодействовал.
Первое решение:
Вы можете сделать вашу функцию Func1()
шаблоном функции:
template<typename T>
bool Func1(int Arg1, T&& Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
return false; // <== DO NOT FORGET A return STATEMENT IN A VALUE-RETURNING
// FUNCTION, OR YOU WILL GET UNDEFINED BEHAVIOR IF FLOWING
// OFF THE END OF THE FUNCTION WITHOUT RETURNING ANYTHING
}
Затем вы можете вызвать его, как хотите:
int main()
{
Func1(12, [](int D) -> bool { return D < 0; } );
}
Второе решение:
Если вы не хотите использовать шаблоны, альтернативой (которая принесет некоторые издержки во время выполнения) является использование std::function
:
#include <functional>
bool Func1(int Arg1, std::function<bool(int)> Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
return false;
}
Еще раз, это позволит вам вызвать Func1()
так, как вы пожелаете:
int main()
{
Func1(12, [](int D) -> bool { return D < 0; } );
}
Для тех, чьи вкусы более традиционны, обратите внимание на то, что не захватывающие lambdas могут преобразовывать в указатели на функции. Таким образом, вы можете написать свою функцию выше:
bool Func1(int Arg1, bool (*Arg2)(int)) { ... }
И он будет корректно работать как для традиционных функций , так и lambdas.