Подтвердить что ты не робот

Лямбда-выражения как параметры шаблона класса

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

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

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 просто отстает в их реализации?

4b9b3361

Ответ 1

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

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

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

Оценка лямбда-выражения приводит к временному присвоению (12.2). Это временное название закрывающий объект. Лямбда-выражение не должно появляться в неопубликованном операнде (раздел 5). [Примечание: A объект замыкания ведет себя как объект функции (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;

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

Ответ 2

@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, если вы измените сравнение равенства, вам лучше изменить хэш, чтобы он соответствовал поведению. Объекты, которые сравнивают равные, должны иметь один и тот же хэш.

Ответ 3

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

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

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

Ответ 4

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