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

如何使用 lambda 表达式作为模板参数?例如。作为初始化 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++?)

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


【解决方案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);
    

    干杯

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多