C++ Lambda 表达式作为类模板参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5849059/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:01:22  来源:igfitidea点击:

Lambda expressions as class template parameters

c++templateslambdac++11

提问by Channel72

Can lambda expressions be used asclass template parameters? (Note this is a very different question than this one, which asks if a lambda expression itself can be templated.)

可以lambda表达式被用来作为类模板参数?(请注意,这是一个与this非常不同的问题,后者询问是否可以对 lambda 表达式本身进行模板化。)

I'm asking if you can do something like:

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

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

This would be useful in cases where, for example, a class template has various parameters like equal_toor something, which are usually implemented as one-liner functors. For example, suppose I want to instantiate a hash table which uses my own custom equality comparison function. I'd like to be able to say something like:

这在以下情况下很有用,例如,类模板具有各种参数,例如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;

But I tested this on GCC 4.4 and 4.6, and it doesn't work, apparently because the anonymous type created by a lambda expression doesn't have a default constructor. (I recall a similar issue with boost::bind.) Is there some reason the draft standard doesn't allow this, or am I wrong and it is allowed but GCC is just behind in their implementation?

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

采纳答案by Xeo

I'm asking if you can do something like:

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

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

No you can't, because lambda expressions shall not appear in an unevaluated context (such as decltypeand sizeof, amongst others). C++0x FDIS, 5.1.2 [expr.prim.lambda] p2

不,您不能,因为 lambda 表达式不应出现在未计算的上下文中(例如decltypeandsizeof等)。C++0x FDIS, 5.1.2 [expr.prim.lambda] p2

The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the closure object. A lambda-expression shall not appear in an unevaluated operand(Clause 5). [ Note: A closure object behaves like a function object (20.8).—end note ] (emphasis mine)

对 lambda 表达式的求值产生一个 prvalue 临时值 (12.2)。这个临时对象称为闭包对象。lambda 表达式不应出现在未计算的操作数中(第 5 条)。[注意:闭包对象的行为类似于函数对象(20.8)。-尾注] (强调我的)

You would need to first create a specific lambda and then use decltype on that:

您需要首先创建一个特定的 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;

That is because each lambda-derived closure object could have a completely different type, they're like anonymousfunctions after all.

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

回答by Matthieu M.

@Xeo gave you the reason, so I'll give you the work around.

@Xeo 给了你原因,所以我会给你解决办法。

Often times you do not wish to name a closure, in this case, you can use std::function, which is a type:

很多时候你不想命名一个闭包,在这种情况下,你可以使用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;

Note that it captures exactly the signature of the function, and no more.

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

Then you may simply write the lambda when building the map.

然后您可以在构建地图时简单地编写 lambda。

Note that with unordered_map, if you change the equality comparison, you'd better change the hash to match the behavior. Objects that compare equal shall have the same hash.

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

回答by Ben Voigt

You can't do this with a closure, because the state is not contained in the type.

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

If your lambda is stateless (no captures), then you should be ok. In this case the lambda decays to an ordinary function pointer, which you can use as a template argument instead of some lambda type.

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

gcc doesn't like it though. http://ideone.com/bHM3n

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

回答by Ap31

C++20 answer: yes!

C++20 答案:是的!

You can totally do something like

你完全可以做类似的事情

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

As c++20 allows stateless lambdas in unevaluated contexts.

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

回答by Puppy

You will have to use either a run-time abstract type, like std::function, or create the type as a local variable or as part of a templated class.

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