Lambda表达式作为类模板参数

Cha*_*l72 60 c++ lambda templates c++11

可以lambda表达式被用来作为类模板参数?(注意这是比一个非常不同的问题这一个,其中询问是否lambda表达式本身可以模板化).

我问你是否可以这样做:

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

这在例如类模板具有各种参数之类的情况下是有用的,这些参数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;
Run Code Online (Sandbox Code Playgroud)

但我在GCC 4.4和4.6上对此进行了测试,但它不起作用,显然是因为lambda表达式创建的匿名类型没有默认构造函数.(我记得有一个类似的问题boost::bind.)是否有某些原因标准草案不允许这样做,或者我错了并且允许但是GCC仅仅落后于它们的实施?

Xeo*_*Xeo 53

我问你是否可以这样做:

Foo<decltype([]()->void { })> foo;
Run Code Online (Sandbox Code Playgroud)

不,你不能,因为lambda表达式不得出现在未计算的情况下(如decltypesizeof,除其他外).C++ 0x FDIS,5.1.2 [expr.prim.lambda] p2

lambda表达式的评估导致prvalue临时(12.2).这个临时对象称为闭包对象.lambda表达式不应出现在未评估的操作数中(第5条).[注意:闭包对象的行为类似于函数对象(20.8).- end note] (强调我的)

您需要先创建一个特定的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;
Run Code Online (Sandbox Code Playgroud)

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

  • @Xeo"匿名函数"并不是真正的函数.这应该大声说出来,因为它令人困惑. (2认同)
  • @There:第5.1.2节,第1,2和6段.p1和p2解释了lambdas创建函数对象(仿函数).p6表示非捕获lambda对应于普通的自由函数(使其能够存储在普通的函数指针中). (2认同)

Mat*_* M. 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;
Run Code Online (Sandbox Code Playgroud)

请注意,它会准确捕获函数的签名,而不再捕获.

然后你可以在构建地图时简单地编写lambda.

请注意unordered_map,如果更改相等比较,则最好更改哈希以匹配行为.比较相等的对象应具有相同的哈希值.

  • 没有用,`std :: function`不包含实际lambda类型的`operator()`行为. (3认同)

Ap3*_*p31 9

C++20 答案:是的!

你完全可以做类似的事情

Foo<decltype([]()->void { })> foo;
Run Code Online (Sandbox Code Playgroud)

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


Ben*_*igt 5

你不能用一个闭包来做这个,因为状态不包含在类型中.

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

gcc不喜欢它. http://ideone.com/bHM3n

  • @ijprest - 不,您不能使用 Xeo 发布的解决方法来解决此问题。尝试它会出现如下错误:“错误:使用已删除的函数 '&lt;lambda(int, int)&gt;::&lt;lambda&gt;()'”。 (2认同)