Lambda 表达式作为类模板参数

2021-12-13 00:00:00 lambda templates c++ c++11

可以将 lambda 表达式用作作为类模板参数吗?(请注意,这是一个与 this one 截然不同的问题,后者询问是否lambda 表达式本身可以被模板化.)

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

我在问你是否可以这样做:

I'm asking if you can do something like:

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

这在以下情况下很有用,例如,类模板具有各种参数,例如 equal_to 或其他东西,这些参数通常被实现为单行函子.例如,假设我想实例化一个哈希表,它使用我自己的自定义相等比较函数.我希望能够这样说:

This would be useful in cases where, for example, a class template has various parameters like equal_to or 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:

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 只是在他们的实施中落后?

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?

推荐答案

从 C++20 开始,这个答案现在已经过时了.C++20 在未评估的上下文中引入了无状态 lambdas1:

As of C++20, this answer is now outdated. C++20 introduces stateless lambdas in unevaluated contexts1:

此限制最初旨在防止 lambda 出现在签名中,因为 lambda 需要具有唯一类型,因此这会打开一罐蠕虫进行重整.然而,限制比它需要的要强得多,没有它确实可以达到同样的效果

This restriction was originally designed to prevent lambdas from appearing in signatures, which would have opened a can of worm for mangling because lambdas are required to have unique types. However, the restriction is much stronger than it needs to be, and it is indeed possible to achieve the same effect without it

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

Some restrictions are still in place (e.g. lambdas still can't appear on function signatures), but the described usecase is now completely valid and the declaration of a variable is no longer necessary.

我在问你是否可以这样做:

I'm asking if you can do something like:

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

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

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

对 lambda 表达式的求值产生一个 prvalue 临时值 (12.2).这个临时被称为闭包对象.lambda 表达式不得出现在未计算的操作数中(第 5 条).[注:A闭包对象的行为就像一个函数对象(20.8).――尾注](强调我的)

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,然后对其使用 decltype:

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

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 派生的闭包对象都可以有完全不同的类型,毕竟它们就像匿名函数.

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

相关文章