Как использовать лямбда-выражение в качестве параметра шаблона?

Как использовать лямбда-выражение в качестве параметра шаблона? Например. как класс сравнения, инициализирующий std :: set.

Следующее решение должно работать, поскольку лямбда-выражение просто создает анонимную структуру, которая должна подходить в качестве параметра шаблона. Однако порождает много ошибок.

Пример кода:

struct A {int x; int y;};
std::set <A, [](const A lhs, const A &rhs) ->bool {
    return lhs.x < rhs.x;
    } > SetOfA;

Вывод ошибок (я использую компилятор g ++ 4.5.1 и флаг компиляции --std = c ++ 0x):

error: ‘lhs’ cannot appear in a constant-expression
error: ‘.’ cannot appear in a constant-expression
error: ‘rhs’ cannot appear in a constant-expression
error: ‘.’ cannot appear in a constant-expression
At global scope:
error: template argument 2 is invalid

Это ожидаемое поведение или ошибка в GCC?

ИЗМЕНИТЬ

Как кто-то заметил, я неправильно использую лямбда-выражения, поскольку они возвращают экземпляр анонимной структуры, на которую они ссылаются.

Однако исправление этой ошибки не решает проблемы. Я получаю lambda-expression in unevaluated context ошибку для следующего кода:

struct A {int x; int y;};
typedef decltype ([](const A lhs, const A &rhs) ->bool {
    return lhs.x < rhs.x;
    }) Comp;
std::set <A, Comp > SetOfA;

person Community    schedule 28.09.2010    source источник
comment
Я пометил это как c ++ 0x. Это кажется более подходящим и требует более точных ответов.   -  person JoshD    schedule 28.09.2010
comment
@JoshD Разве это не должно быть помечено как «c ++»? 0x в конечном итоге станет новым стандартом, и я бы не хотел, чтобы люди в будущем пропустили этот вопрос, потому что они забыли, что правильным тегом был c ++ 0x, а не c ++. (Или SO собирается в какой-то момент перенести все теги c ++ 0x на c ++?)   -  person KitsuneYMG    schedule 28.09.2010


Ответы (4)


Второй параметр шаблона std::set ожидает тип, а не выражение, так что вы просто используете его неправильно.

Вы можете создать такой набор:

auto comp = [](const A& lhs, const A& rhs) -> bool { return lhs.x < rhs.x; };
auto SetOfA = std::set <A, decltype(comp)> (comp);
person kennytm    schedule 28.09.2010
comment
лямбда-выражение ‹i› - это действительно тип. Это просто еще один способ объявить анонимную структуру с помощью определенного оператора (). Я не использую лямбда в качестве спецификатора типа: std :: ser ‹‹b› A ‹/b›, ....› B - person ; 28.09.2010
comment
@buratina: Если это был тип, то [](){} x; должно быть действительным объявлением. Лямбда-выражение - это просто экземпляр этой анонимной структуры. Вам нужен decltype, чтобы получить этот тип. - person kennytm; 28.09.2010
comment
ОК, теперь он очищен :) но decltype тоже как-то не работает - person ; 28.09.2010
comment
@buratinas: попробуйте поставить точку с запятой после struct A {int x; int y;}. - person kennytm; 28.09.2010
comment
Что касается вашего примера: мне нужно, чтобы std :: set находился в typedef, поэтому любые временные переменные не подходят. / n / n Почему-то добавление точки с запятой ничего не меняет. Код должен был быть взломан с самого начала, не так ли? - person ; 28.09.2010
comment
@buratinas: (1) У меня работает: pastie.org/1186017. (2) Почему бы не реализовать operator< для типа A? - person kennytm; 28.09.2010
comment
@KennyTM Мне бы хотелось, чтобы лямбда-выражения были просто типами, чтобы приведенный вами пример мерзости - [](){}x; - действительно был легальным C ++. Почему Perl должен получать все самое интересное? На (немного) более серьезном замечании, означает ли это, что decltype([](){}) x действителен? - person KitsuneYMG; 29.09.2010
comment
@KennyTM (re 1) Я имел в виду, что не хочу, чтобы в моем коде был объект «auto comp». (re 2) В конкретном случае, с которым я имею дело, A - это const Sometype *. IIRC Я не могу перегружать операторы сравнения для указателей. - person ; 29.09.2010
comment
@kts: В принципе, да, но из-за §5.1.2 / 2 лямбда не может появляться внутри decltype. - person kennytm; 29.09.2010
comment
@buratinas: Вы не можете пропустить этот comp, если не создадите функцию-оболочку для создания std::set. - person kennytm; 29.09.2010
comment
Этот код несовместим, поскольку лямбда-объекты не являются CopyConstructible. std::set <A, decltype(comp)> (comp); берет объект comp и копирует его в другой объект в set того же типа. Это работа для std::function - см. Ответ Кена. - person Potatoswatter; 01.10.2010
comment
Вероятно, причина, по которой лямбда-выражение не может быть в неоцененном операнде, заключается в том, что реализация может (и вероятно) сделать все типы замыкания уникальными, поэтому любое значение, которое вы извлекаете из неоцененного операнда, не может быть преобразовано в другой тип. - person Potatoswatter; 01.10.2010
comment
@ Картофель: я не могу найти доказательств того, что лямбда-объекты не являются CopyConstructible. Единственный релевантный текст - §5.1.2 / 19. Тип закрытия, связанный с лямбда-выражением, имеет конструктор по умолчанию удаленный (8.4.3) и оператор присваивания удаленной копии. Он имеет неявно объявленный конструктор копирования (12.8) и может иметь неявно объявленный конструктор перемещения (12.8). - person kennytm; 01.10.2010
comment
@Kenny: Хм, я только что не дочитал до этого места. Требуется ли реализация, чтобы не делать ничего, что могло бы помешать работе неявно определенного конструктора? Странно, что они не использовали имена требований шаблона-аргумента, то есть Movable и CopyConstructible. В любом случае std::function - тоже более легкий выбор. - person Potatoswatter; 01.10.2010
comment
Ах, template<class F> function::function(F) требует, чтобы лямбды были CopyConstructible. 20.8.14.2.1 / 9, Стандарт становится слишком большим ... - person Potatoswatter; 01.10.2010
comment
По состоянию на 19 апреля 2014 г. при использовании подхода, описанного в этом ответе, в VS 2013 обнаружена ошибка, которая дает error C3497: you cannot construct an instance of a lambda при попытке передать экземпляр лямбда-выражения без ничего в его списке захвата. Обходной путь - заключить определение labmda в std::function. - person Dan Nissenbaum; 19.04.2014

Для компараторов, используемых таким образом, вам все равно лучше использовать подход, отличный от 0x:

struct A { int x; int y; };

struct cmp_by_x {
  bool operator()(A const &a, A const &b) {
    return a.x < b.x;
  }
};

std::set<A, cmp_by_x> set_of_a;

Однако в 0x вы можете сделать cmp_by_x локальным типом (т.е. определить его внутри функции), когда это более удобно, что запрещено текущим C ++.

Кроме того, ваше сравнение рассматривает A (x = 1, y = 1) и A (x = 1, y = 2) как эквивалентные. Если это нежелательно, вам нужно включить другие значения, которые способствуют уникальности:

struct cmp_by_x {
  bool operator()(A const &a, A const &b) {
    return a.x < b.x || (a.x == b.x && a.y < b.y);
  }
};
person Community    schedule 30.09.2010

Не уверен, что это то, о чем вы спрашиваете, но сигнатура лямбды, которая возвращает RetType и принимает InType, будет:

std::function<RetType(InType)>

(Убедитесь, что #include <functional>)

Вы можете сократить это, используя typedef, но я не уверен, что вы можете использовать decltype, чтобы не выяснять фактический тип (поскольку лямбды, очевидно, не могут использоваться в этом контексте).

Итак, ваш typedef должен быть:

typedef std::function<bool(const A &lhs, const A &rhs)> Comp

or

using Comp = std::function<bool(const A &lhs, const A &rhs)>;
person Ken Simon    schedule 29.09.2010
comment
+1, потому что это решение, но std::function - это просто тип держателя. Вы можете преобразовать лямбду в function и получить объект, указывающий на лямбду, но это не его исходный тип. - person Potatoswatter; 01.10.2010
comment
Изменить: функция не указывает на лямбду, она ее содержит. Но в любом случае это был не оригинальный тип. - person Potatoswatter; 01.10.2010
comment
Ах, приятно знать. Я так понимаю, что синтаксис inline-lambda создает какой-то случайно сгенерированный тип, так что нет двух абсолютно одинаковых? (Так это делает C #, IIRC.) - person Ken Simon; 01.10.2010
comment
+1 За полезный метод получения универсального типа лямбда-функции (без предварительного помещения лямбда-функции в переменную). - person Interarticle; 17.10.2013

проблема в том, что последний параметр шаблона - это тип, а не объект, поэтому вы можете сделать следующее

    std::set <A, std::fuction<bool(const A &,const A &)>> 
              SetOfA([](const A lhs, const A &rhs) ->bool {
                                                             return lhs.x < rhs.x;
                                                          } > SetOfA;

для упрощения можно сделать следующее:

auto func = SetOfA([](const A lhs, const A &rhs) ->bool { return lhs.x < rhs.x;}
set <A,decltype(func)> SetOfA(func);

ваше здоровье

person yaron kahanovitch    schedule 25.12.2014