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

Думая о лямбда-выражениях как о «синтаксическом сахаре» для вызываемых объектов, можно ли выразить безымянный базовый тип?

Пример:

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;
 David Rodríguez - dribeas05 окт. 2010 г., 23:52
@Donotalo: Не только это, но вы можете связать лямбда-выражение с переменной, если вы хотите с помощьюstd::function с соответствующим набором параметров.
 jalf05 окт. 2010 г., 22:21
@ Донотало: так вы говорите, что, поскольку есть случаи, когда это не лучшее решение, оно можетникогда быть хорошим решением? Причина лямбды в том, что часто Ойю нужен только этот компаратор водин место. Если тебе это нужно 20 раз, то я не думаюкто-нибудь когда-либо предлагал выразить это как лямбда.
 Donotalo05 окт. 2010 г., 22:11
это одна из причин, по которой мне не понравилось лямбда-выражение. Предположим, вам нужен тот же компаратор в 20 областях вашего кода. Вы бы написали компаратор 20 раз или написали бы один раз и передали бы как указатель функтора / функции?
 Tomaka1705 окт. 2010 г., 22:22
@Donotalo: лямбды обычно достаточно просты, чтобы их можно было копировать в разных местах вашей программы; Повторное использование кода хорошо для нетривиальных функций, но вы не должны использовать лямбду для них
 Donotalo05 окт. 2010 г., 22:26
хорошо понял на самом деле мне никогда не нужно было что-то вроде лямбды в одном месте. обычно я определяюoperator<() для моих классов, которые должны быть отсортированы. вот почему я не понимаю, зачем нужна лямбда.

Ответы на вопрос(4)

Вы можете использовать небольшой класс lambda_wrapper <>, чтобы обернуть лямбду с небольшими затратами. Это намного быстрее, чем std :: function, потому что нет вызова виртуальной функции и динамического выделения памяти. Оболочка работает, выводя список аргументов лямбда и тип возвращаемого значения.

#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;
}
 aschepler22 июл. 2017 г., 19:16
Нет, вы не можете хранить два разныхlambda_wrappers в контейнере только две копии одной и той же лямбды. Что вы также можете сделать сauto лямбда напрямую.
 aschepler22 июл. 2017 г., 00:09
Какauto func = make_lambda_wrapper([](int y, int x) { return x>y; }); полезнее, чем простоauto func = [](int y, int x) { return x>y; };?
 barney22 июл. 2017 г., 09:25
Тип @aschepler "auto func" - это анонимный класс, lamda_wrapper - это явный тип класса. Таким образом, вы можете хранить lambda_wrappers в std :: map или установить, например. Нельзя равномерно хранить коллекцию сырых лямбд, потому что каждый из них будет разного типа. Но вы можете определить вектор <lambda_wrapper> и хранить любые лямбды с соответствующей подписью. Конечно, вы можете сделать это с помощью std :: function, но std :: function работает намного медленнее (vptr + atomic lock + heap alloc)
Решение Вопроса

Нет, вы не можете положить это в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; });

Как всегда, сначала код, потом профиль :)

 Rakete111108 апр. 2018 г., 07:24
C ++ 20 теперь позволяет лямбды в неоцененном операнде :)
 Cheers and hth. - Alf22 мая 2015 г., 23:32
@ JohannesSchaub-litb: У каждого модуля перевода уже есть уникальный идентификатор для анонимного пространства имен, поэтому уникальность не является проблемой. Но есть и вариант использования. Я просто считаю это ограничение странным, по-видимому, без веской причины.
 Cheers and hth. - Alf22 мая 2015 г., 17:56
«Лямбда-выражение не должно появляться в неоцененном операнде» кажется глупым, плохо продуманным. Зачем это, вы знаете?
 Johannes Schaub - litb22 мая 2015 г., 21:55
Я думаю, что это было о таких случаях, какtemplate<typename T> illformed_test<decltype([]{ T t; t.f(); }), void> f(); который разработчик мог бы написать, чтобы потребоватьT быть конструируемым по умолчанию и иметь функциюf.
 Omnifarious05 окт. 2010 г., 22:26
Я думал об использованииauto а такжеdeclytype в сочетании таким образом, когда я читаю вопрос.посмеиваться  Использование этой комбинации для извлечения типа выражения, которое на самом деле не имеет видимого типа, довольно забавно.
 Johannes Schaub - litb22 мая 2015 г., 21:53
@ Cheersandhth.-Alf хм, ты говоришь что-то подобное в TU[]{}; тип лямбда имеет1 где-то и в[]{};[]{}; тип второго ламба имеет2 где-то? Я не думаю, что это решает проблему. Разрешено переопределять шаблоны в TU, которые выглядят по-разному, если сам шаблон выглядит и ведет себя одинаково. Если один TU имеет больше лямбда-выражений перед шаблоном, чем другой, шаблоны будут искажены по-другому, если они будут использовать тип лямбда-выражения для искажения лямбда-выражения. Не хорошая идея.
 Johannes Schaub - litb22 мая 2015 г., 20:45
@ Cheersandhth.-Alf для предотвращения необходимости искажения его в параметре шаблона, чтобы он был частью подписи шаблона функции (поэтому ABI должен был бы определять искажение не только для выражений, но и для произвольных операторов) , Так как каждый expr является объектом уникального типа, я не уверен, использовать ли его в sizeof или decltype в любом случае. Вот как я это понимаю, но я могу ошибаться, я не эксперт в этом. Ричард Смит из группы лягушек знал бы.
 Cheers and hth. - Alf22 мая 2015 г., 21:14
@ JohannesSchaub-litb: Спасибо за размышления. Я думаю, тривиально создавать систематические имена классов для лямбд, просто нумеруя их в каждой единице перевода. Но, учитывая тип, нет способа создать лямбду с захватами. С третьей стороны, тип доступен, когда лямбда-выражение передается в шаблон функции (вывод типа аргумента), поэтому, если речь идет о безопасности, тогда тип должен быть уже безопасным. Не уверен насчет варианта использования ... ;-)

По крайней мере, в Microsoft Visual Studio (я не пробовал это с другими компиляторами), и если вы ничего не захватываете, тип выглядит как обычный указатель на функцию:

std::string (*myFunctionPointer)(int x) = [] (int x) {
  char buffer[10];
  return std::string("Test ") + itoa(x, buffer, 10);
};
std::string testOutput = myFunctionPointer(123);

Прямой ответ на ваш вопрос: Нет.

Вам нужно будет использовать то, что можно назначить из любого типа, напоминающего функтор, который имеет четко определенный тип. Одним из примеров является std :: function, как показано в ответе sbi. Это не тип лямбда-выражения.

Ваш ответ на вопрос