Lambda表达式作为类模板参数

问题描述 投票:63回答:5

可以使用lambda表达式 作为 类模板参数? (注意,这与 这个我问的是lambda表达式本身是否可以被模板化)。)

我在问你是否可以做这样的事情。

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

这将会很有用,比如说,一个类模板有各种参数,比如: equal_to 或其他什么东西,它们通常被实现为单行的漏斗函数。 例如,假设我想实例化一个哈希表,这个表使用了我自己定制的平等比较函数。 我希望能够说这样的话:"我在GCC 4.4和4.4上测试了这个函数。

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上测试过了,它不能用,显然是因为lambda表达式创建的匿名类型没有默认的构造函数。 (我记得类似的问题在 boost::bind.) 是不是有什么原因,标准草案不允许这样做,还是我错了,它是允许的,只是GCC在实现上落后了?

c++ templates lambda c++11
5个回答
56
投票

从C++20开始,这个答案已经过时了。C++20引入了无状态lambdas在未评估上下文中的应用。1:

这一限制最初是为了防止在签名中出现lambdas,因为lambdas必须有独特的类型,这将为篡改打开一扇虫子的大门。然而,这个限制比它所需要的要强得多,而且确实可以在没有这个限制的情况下达到同样的效果。

一些限制仍然存在(例如,lambdas仍然不能出现在函数签名上),但现在描述的用例是完全有效的,不再需要声明一个变量。


我问你能不能做这样的事情。

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

不,你不能,因为lambda表达式不应该出现在一个未评估的上下文中(例如: decltypesizeof其中包括).C++0x FDIS, 5.1.2 [expr.prim.lambda] p2

对 lambda 表达式的评估会产生一个 prvalue 临时对象 (12.2)。这个临时对象被称为closure对象。一个lambda表达式不应出现在一个未被评价的操作数中。 (条款5). [注:封闭对象的行为与函数对象相同(20.8)。(重点是我)

你需要先创建一个特定的lambda,然后再对其使用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;

那是因为每个lambda派生的闭包对象可以有一个完全不同的类型,它们就像... 匿名的 毕竟是函数。


9
投票

@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;

请注意,它准确地捕捉到了函数的签名,仅此而已。

然后你可以在构建映射时简单地写出lambda。

请注意,对于 unordered_map如果你改变了平等比较,你最好改变哈希值来匹配行为。比较相等的对象应具有相同的哈希值。


5
投票

你不能用闭包来做这件事,因为状态不包含在类型中。

如果你的lambda是无状态的(没有捕获),那么你应该可以。 在这种情况下,lambda 衰减为一个普通的函数指针,你可以用它作为模板参数而不是某个 lambda 类型。

不过gcc并不喜欢这样。 http:/ideone.comHM3n


2
投票

C++20答案:是的!

你完全可以做这样的事情

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

由于c++20允许在未评估的上下文中使用无状态的lambdas。


0
投票

你将不得不使用一个运行时的抽象类型,如 std::function或将该类型创建为一个局部变量或作为模板类的一部分。

© www.soinside.com 2019 - 2024. All rights reserved.