如何使用 lambda 表达式作为模板参数?

Posted

技术标签:

【中文标题】如何使用 lambda 表达式作为模板参数?【英文标题】:How to use a lambda expression as a template parameter? 【发布时间】:2011-04-18 03:40:55 【问题描述】:

例如。作为初始化 std::set 的比较类。

以下解决方案应该有效,因为 lambda 表达式仅创建一个匿名结构,它应该适合作为模板参数。但是,会产生很多错误。

代码示例:

struct A int x; int y;;
std::set <A, [](const A lhs, const A &rhs) ->bool 
    return lhs.x < rhs.x;
     > SetOfA;

错误输出(我使用的是 g++ 4.5.1 编译器和 --std=c++0x 编译标志):

error: ‘lhs’ cannot appear in a constant-expression
error: ‘.’ cannot appear in a constant-expression
error: ‘rhs’ cannot appear in a constant-expression
error: ‘.’ cannot appear in a constant-expression
At global scope:
error: template argument 2 is invalid

这是预期的行为还是 GCC 中的错误?

编辑

正如有人指出的那样,我错误地使用了 lambda 表达式,因为它们返回了他们所指的匿名结构的 instance

但是,修复该错误并不能解决问题。我收到以下代码的lambda-expression in unevaluated context 错误:

struct A int x; int y;;
typedef decltype ([](const A lhs, const A &rhs) ->bool 
    return lhs.x < rhs.x;
    ) Comp;
std::set <A, Comp > SetOfA;

【问题讨论】:

我将其标记为 c++0x。它似乎更合适,应该得到更好的答案。 @JoshD 它不应该也被标记为“c++”吗? 0x 最终将成为新标准,我不希望未来的人们错过这个问题,因为他们忘记了正确的标签是 c++0x 而不是 c++。 (或者,是否会在某个时候将所有 c++0x 标签迁移到 c++?) 【参考方案1】:

std::set 的第二个模板参数需要一个 type,而不是 expression,所以只是你用错了。

你可以这样创建集合:

auto comp = [](const A& lhs, const A& rhs) -> bool  return lhs.x < rhs.x; ;
auto SetOfA = std::set <A, decltype(comp)> (comp);

【讨论】:

lambda 表达式 is 确实是一种类型。这只是用定义的运算符 () 声明匿名结构的另一种方式。我不使用 lambda 作为类型说明符:std::ser A, ....> B @buratina:如果它一个类型,那么[]() x;应该是一个有效的声明。 lambda 表达式只是该匿名结构的 instance。您需要decltype 才能获得该类型。 好的,现在它被清除了 :) 但 decltype 也不知何故不起作用 @buratinas:尝试在struct A int x; int y; 之后添加分号。 @KennyTM 我有点希望 lambda 表达式只是类型,所以您发布的示例可憎——[]()x;——确实是合法的 c++。为什么 perl 应该有所有的乐趣?在(稍微)更严肃的一点上,这是否意味着 decltype([]()) x 有效?【参考方案2】:

对于以这种方式使用的比较器,您最好使用非 0x 方法:

struct A  int x; int y; ;

struct cmp_by_x 
  bool operator()(A const &a, A const &b) 
    return a.x < b.x;
  
;

std::set<A, cmp_by_x> set_of_a;

但是,在 0x 中,您可以将 cmp_by_x 设为局部类型(即在函数中定义它),这样更方便,这是当前 C++ 所禁止的。

此外,您的比较将 A(x=1, y=1) 和 A(x=1, y=2) 视为等效。如果不需要,您需要包含有助于唯一性的其他值:

struct cmp_by_x 
  bool operator()(A const &a, A const &b) 
    return a.x < b.x || (a.x == b.x && a.y < b.y);
  
;

【讨论】:

【参考方案3】:

不确定这是否是您要问的,但返回 RetType 并接受 InType 的 lambda 的签名将是:

std::function<RetType(InType)>

(一定要#include &lt;functional&gt;

您可以通过使用 typedef 来缩短它,但我不确定您是否可以使用 decltype 来避免找出实际类型(因为 lambdas 显然不能在该上下文中使用。)

所以你的 typedef 应该是:

typedef std::function<bool(const A &lhs, const A &rhs)> Comp

using Comp = std::function<bool(const A &lhs, const A &rhs)>;

【讨论】:

+1 因为这是解决方案,但 std::function 只是一个持有者类型。您可以将 lambdaconvert 转换为 function 并获得一个 pointing 指向 lambda 的对象,但这不是它的原始类型。 编辑:该函数不指向 lambda,它包含它。但它也不是原来的类型。 啊,很高兴知道。我认为 inline-lambda 语法会产生一些随机生成的类型,因此没有两个完全相同? (这就是 C# 的做法,IIRC。) +1 获取通用 lambda 函数类型的有用方法(无需先将 lambda 函数放入变量中)。【参考方案4】:

问题是最后一个模板参数是类型而不是对象,所以您可能需要执行以下操作

    std::set <A, std::fuction<bool(const A &,const A &)>> 
              SetOfA([](const A lhs, const A &rhs) ->bool 
                                                             return lhs.x < rhs.x;
                                                           > SetOfA;

为了更简单,您可以执行以下操作:

auto func = SetOfA([](const A lhs, const A &rhs) ->bool  return lhs.x < rhs.x;
set <A,decltype(func)> SetOfA(func);

干杯

【讨论】:

以上是关于如何使用 lambda 表达式作为模板参数?的主要内容,如果未能解决你的问题,请参考以下文章

带有 lambda 作为每个实例化的唯一默认参数的模板

如何创建一个接受多个 lambda 表达式作为参数的方法?

Java中Lambda表达式基础及使用详解

kotlin lambda 表达式作为可选参数

如何使用lambda表达式捕获局部变量?

kotlin方法传入lambda表达式参数并调用invoke什么意思