Лямбда-выражения как параметры шаблона класса
Можно ли использовать лямбда-выражения в качестве параметров шаблона класса? (Обратите внимание, что это совсем другой вопрос, чем этот, который спрашивает, может ли быть шаблонное выражение сам по себе.)
Я спрашиваю, можете ли вы сделать что-то вроде:
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 просто отстает в их реализации?
Ответы
Ответ 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
, либо создать его как локальную переменную или как часть шаблонного класса.