如何使用 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 <functional>
)
您可以通过使用 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 表达式作为模板参数?的主要内容,如果未能解决你的问题,请参考以下文章