【问题标题】:How does generic lambda work in C++14?通用 lambda 在 C++14 中如何工作?
【发布时间】:2013-06-18 11:38:18
【问题描述】:

泛型 lambda 在 C++14 标准中如何工作(auto 关键字作为参数类型)?

它是基于 C++ 模板,其中每个不同的参数类型编译器生成一个具有相同主体但替换类型的新函数(编译时多态性)还是更类似于 Java 的泛型(类型擦除)?

代码示例:

auto glambda = [](auto a) { return a; };

【问题讨论】:

  • 已修复为 C++14,最初使用有问题的 C++11

标签: c++ lambda auto c++14


【解决方案1】:

C++14 中引入了通用 lambda。

简单地说,由 lambda 表达式定义的闭包类型将具有 模板化 调用运算符,而不是 C++11 的 lambda 的常规、非模板调用运算符(当然,当auto 在参数列表中至少出现一次时)。

所以你的例子:

auto glambda = [] (auto a) { return a; };

将使glambda 成为这种类型的实例:

class /* unnamed */
{
public:
    template<typename T>
    T operator () (T a) const { return a; }
};

C++14 标准草案 n3690 的第 5.1.2/5 段规定了如何定义给定 lambda 表达式的闭包类型的调用运算符:

非泛型 lambda 表达式的闭包类型具有公共内联函数调用运算符 (13.5.4) 其参数和返回类型由 lambda 表达式的参数声明子句描述 和尾随返回类型。 对于泛型 lambda,闭包类型有一个公共的内联函数调用 操作员成员模板(14.5.2),其模板参数列表由一种发明类型模板参数组成 对于 lambda 的 parameter-declaration-clause 中每次出现的 auto,按出现顺序。 如果相应的参数声明声明,则发明的类型模板参数是参数包 一个函数参数包(8.3.5)。函数调用的返回类型和函数参数 运算符模板派生自 lambda-expression 的 trailing-return-type 和 parameter-declarationclause 通过将参数声明子句的 decl-specifiers 中每次出现的 auto 替换为 相应发明的模板参数的名称。

最后:

它是类似于模板,其中为每个不同的参数类型编译器生成具有相同主体但类型不同的函数,还是更类似于 Java 的泛型?

如上段所述,通用 lambda 只是具有模板化调用运算符的独特、未命名仿函数的语法糖。那应该回答你的问题:)

【讨论】:

  • 但是,它们也允许使用模板方法在本地定义的类。这是新的。
  • @Yakk:C++11 对函数局部模板的限制不是已经完全取消了吗?
  • @phresnel:不,这个限制还没有解除
  • @AndyProwl:我意识到我的错误。确实提升的是使用本地类型作为模板参数(如int main () { struct X {}; std::vector&lt;X&gt; x; }
  • @phresnel:对,确实改变了
【解决方案2】:

很遗憾,它们不是 C++11 的一部分 (http://ideone.com/NsqYuq):

auto glambda = [](auto a) { return a; };

int main() {}

使用 g++ 4.7:

prog.cpp:1:24: error: parameter declared ‘auto’
...

然而,它可能按照Portland proposal for generic lambdas在 C++14 中实现的方式:

[](const& x, & y){ return x + y; }

这将在很大程度上产生一个匿名仿函数类的通常创建,但由于缺少类型,编译器会发出一个模板化成员-operator()

struct anonymous
{
    template <typename T, typename U>
    auto operator()(T const& x, U& y) const -> decltype(x+y)
    { return x + y; }
};

或根据较新的提案Proposal for Generic (Polymorphic) Lambda Expressions

auto L = [](const auto& x, auto& y){ return x + y; };

--->

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

所以是的,对于参数的每一个排列,都会产生一个新的实例化,但是,该仿函数的成员仍然是共享的(即捕获的参数)。

【讨论】:

  • 允许删除 type-specifier 的提议非常荒谬。
  • 他们选择了g++-4.9。您需要提供-std=c++1y
  • 我没有意识到 ideone 还没有 gcc-4.9 和 C++14。
  • 问题:这个auto和经典的auto有同样的扣分规则吗?如果我们引用模板化的类比,那就意味着 auto 不是 auto,它与模板类型推导的规则相同。那么问题来了:模板推演是否等同于auto
  • @v.oddou:“经典汽车”很好。对我来说,“经典自动”意味着“堆栈变量”,并且曾经与staticregister 对比使用:) 无论如何,是的,使用auto 意味着在引擎盖下会生成一个普通模板。实际上,lambda 将在编译器内部被仿函数类替换,auto 参数意味着将发出 template &lt;T&gt; ... (T ...)
【解决方案3】:

这是一个提议的 C++14 功能(不在 C++11 中),类似于(甚至等效)模板。例如,N3559 提供了这个例子:

例如,这个包含通用 lambda 表达式的语句:

auto L = [](const auto& x, auto& y){ return x + y; };

可能会导致创建闭包类型,以及行为类似于以下结构的对象:

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-17
    • 2015-09-30
    • 2018-04-30
    • 2014-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多