Lambda表达式作为类模板参数

时间:2011-05-01 14:45:06

标签: c++ templates lambda c++11

lambda表达式可以用作为类模板参数吗? (注意这是一个与this one非常不同的问题,它询问lambda表达式本身是否可以模板化。)

我问你是否可以这样做:

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上对此进行了测试,但它并不起作用,显然是因为lambda表达式创建的匿名类型没有默认构造函数。 (我回忆起与boost::bind类似的问题。)是否有某些原因标准草案不允许这样做,或者我是错的并且允许但是GCC仅仅落后于它们的实施?

5 个答案:

答案 0 :(得分:51)

  

我问你是否可以这样做:

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

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

  

lambda表达式的评估导致prvalue临时(12.2)。这个临时称为   闭合对象。 lambda表达式不应出现在未评估的操作数中(第5条)。 [注:A   闭包对象的行为类似于函数对象(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;

这是因为每个lambda派生的闭包对象都可以有完全不同的类型,毕竟它们就像 anonymous 一样。

答案 1 :(得分: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,如果更改相等比较,则最好更改哈希以匹配行为。比较相等的对象应具有相同的散列。

答案 2 :(得分:5)

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

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

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

答案 3 :(得分:3)

C ++ 20答案:是的!

您完全可以做类似的事情

std::vector

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

答案 4 :(得分:0)

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