Можно ли выразить "тип" лямбда-выражения?

Размышляя о лямбда-выражениях как "синтаксическом сахаре" для вызываемых объектов, может ли быть выражен неназванный базовый тип?

Пример:

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;
}