Лямбда-выражения как параметры шаблона класса - PullRequest
59 голосов
/ 01 мая 2011

Можно ли использовать лямбда-выражения в качестве параметров шаблона класса?(Обратите внимание, что это совсем другой вопрос, чем этот , который спрашивает, может ли быть задано само лямбда-выражение.)

Я спрашиваю, можете ли вы сделать что-то вроде:

template <class Functor> 
struct Foo { };
// ...
Foo<decltype([]()->void { })> foo;

Это было бы полезно в тех случаях, когда, например, шаблон класса имеет различные параметры, такие как equal_to или что-то еще, которые обычно реализуются в виде однострочных функторов.Например, предположим, что я хочу создать хеш-таблицу, в которой используется моя собственная функция сравнения на равенство.Я хотел бы иметь возможность сказать что-то вроде:

typedef std::unordered_map<
  std::string,
  std::string,
  std::hash<std::string>,
  decltype([](const std::string& s1, const std::string& s2)->bool 
    { /* Custom implementation of equal_to */ })
  > map_type;

Но я проверял это на GCC 4.4 и 4.6, и это не работает, очевидно, потому что анонимный тип, созданный лямбда-выражением, не 'не имеет конструктора по умолчанию.(Я помню похожую проблему с boost::bind.) Есть ли какая-то причина, по которой проект стандарта не позволяет этого, или я ошибаюсь, и это разрешено, но GCC отстает в своей реализации?

Ответы [ 4 ]

52 голосов
/ 01 мая 2011

Я спрашиваю, можете ли вы сделать что-то вроде:

Foo<decltype([]()->void { })> foo;

Нет, вы не можете, потому что лямбда-выражения не должны появляться в неоцененном контексте (например, decltype и sizeof, среди прочих).C ++ 0x FDIS, 5.1.2 [expr.prim.lambda] p2

Оценка лямбда-выражения приводит к временному prvalue (12.2).Этот временный объект называется объектом закрытия. Лямбда-выражение не должно появляться в неоцененном операнде (пункт 5).[Примечание: закрывающий объект ведет себя как функциональный объект (20.8). - Конечная нота] (выделено мной)

Сначала необходимо создать определенную лямбду, а затем использоватьdecltype для этого:

auto my_comp = [](const std::string& left, const std::string& right) -> bool {
  // whatever
}

typedef std::unordered_map<
  std::string,
  std::string,
  std::hash<std::string>,
  decltype(my_comp)
  > map_type;

Это связано с тем, что каждый лямбда-объект замыкания может иметь совершенно другой тип, они похожи на анонимные функции в конце концов.

9 голосов
/ 01 мая 2011

@ Xeo дал вам причину, поэтому я дам вам обходной путь.

Часто вы не хотите называть замыкание, в этом случае вы можете использовать std::function, чтотип:

typedef std::unordered_map<
  std::string,
  std::string,
  std::hash<std::string>,
  std::function<bool(std::string const&, std::string const&)>
  > map_type;

Обратите внимание, что он точно фиксирует сигнатуру функции, и не более.

Тогда вы можете просто написать лямбду при построении карты.

Обратите внимание, что с unordered_map, если вы измените сравнение на равенство, вам лучше изменить хеш, чтобы он соответствовал поведению.Объекты, сравниваемые равными, должны иметь одинаковый хэш.

5 голосов
/ 01 мая 2011

Вы не можете сделать это с замыканием, потому что состояние не содержится в типе.

Если ваша лямбда не имеет состояния (не захватывает), то с вами все должно быть в порядке. В этом случае лямбда преобразуется в обычный указатель на функцию, который можно использовать в качестве аргумента шаблона вместо некоторого лямбда-типа.

gcc это не нравится. http://ideone.com/bHM3n

0 голосов
/ 01 мая 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...