Можно ли выразить "тип" лямбда-выражения?
Размышляя о лямбда-выражениях как "синтаксическом сахаре" для вызываемых объектов, может ли быть выражен неназванный базовый тип?
Пример:
struct gt {
bool operator() (int l, int r) {
return l > r;
}
} ;
Теперь [](int l, int r) { return l > r; }
является элегантной заменой для вышеуказанного кода (плюс необходимое создание вызываемых объектов gt), но есть ли способ выразить gt (сам тип)?
Простое использование:
std::set<int, gt> s1; // A reversed-order std::set
// Is there a way to do the same using a lambda?
std::set<int, some-magic-here-maybe([](int l, int r) { return l > r; }) > s2;
Ответы
Ответ 1
Нет, вы не можете поместить его в decltype
, потому что
Лямбда-выражение не должно появляться в неопубликованном операнде
Вы можете сделать следующее, хотя
auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> s(n);
Но это действительно уродливо. Обратите внимание, что каждое выражение лямбда создает новый уникальный тип. Если впоследствии вы выполните следующее в другом месте, t
имеет другой тип, чем s
auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> t(n);
Здесь вы можете использовать std::function
, но обратите внимание, что это повлечет за собой крошечную стоимость времени выполнения, поскольку для этого требуется косвенный вызов оператору вызова объекта лямбда-функции. Это, вероятно, незначительно здесь, но может быть значительным, если вы хотите передать объекты функций таким образом, например, std::sort
.
std::set<int, function<bool(int, int)>> s([](int l, int r) { return l > r; });
Как всегда, сначала код, а затем профиль:)
Ответ 2
Прямой ответ на ваш вопрос: Нет.
Вам нужно будет использовать то, что можно назначить из любого типа, похожего на функтор с четко определенным типом. Одним из примеров является std:: function, как показано в ответе sbi. Это, однако, не тип лямбда-выражения.
Ответ 3
Вы можете использовать небольшой класс lambda_wrapper < > , чтобы обернуть лямбда при низких затратах. Это намного быстрее, чем функция std::, потому что нет виртуального вызова функции и распределения динамической памяти.
Wrapper работает, выведя список аргументов лямбда и тип возвращаемого значения.
#include <iostream>
#include <functional>
#include <set>
template <typename T, typename ... Args>
struct lambda_wrapper : public lambda_wrapper<decltype(&T::operator())(Args...)> {};
template <typename L>
struct lambda_wrapper<L> {
private:
L lambda;
public:
lambda_wrapper(const L & obj) : lambda(obj) {}
template<typename... Args>
typename std::result_of<L(Args...)>::type operator()(Args... a) {
return this->lambda.operator()(std::forward<Args>(a)...);
}
template<typename... Args> typename
std::result_of<const L(Args...)>::type operator()(Args... a) const {
return this->lambda.operator()(std::forward<Args>(a)...);
}
};
template <typename T>
auto make_lambda_wrapper(T&&t) {
return lambda_wrapper<T>(std::forward<T>(t));
}
int main(int argc, char ** argv)
{
auto func = make_lambda_wrapper([](int y, int x) -> bool { return x>y; });
std::set<int, decltype(func)> ss(func);
std::cout << func(2, 4) << std::endl;
}