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


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

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

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 54

4 ответа:

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

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

нет, вы не можете, потому что лямбда-выражения не должны появляться в необработанные контексте (например,decltype и sizeof, среди других). C++0x FDIS, 5.1.2 [expr.подтянутый.лямбда] 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;

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

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

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

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

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

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