代码之家  ›  专栏  ›  技术社区  ›  Jeff

基于值捕获语义和允许优化的C++ lambda

  •  3
  • Jeff  · 技术社区  · 6 年前

    当函数实际上只使用隐式捕获对象的某些数据成员时,编译器允许从默认值捕获中省略什么?例如。,

    struct A {
      // some members we care about:
      char x;
      int y;
      // some huge amount of state we do not:
      std::array<bool, 200000> z;
    
      int foo() const { return y + 1 }
    };
    
    void bar() {
      A a;
      // must the entirety of a be copy captured, or is the compiler allowed to pick/prune?
      auto l1 = [=](){ std::cout << a.x << ", " << a.y << std::endl; };
      // ...
    }
    

    同样,如果允许早期评估忽略更广泛的捕获,那么什么时候允许?

    void baz(int i) {
      A a2;
      a2.y = i;
    
      // capture fundamentally only needs 1 int, not all of an A instance.
      auto l2 = [=](){ std::cout << a.foo() << std::endl; }
    }
    

    至少在某些情况下,对一个元素进行部分或完全的复制捕获应该没有超出lambda大小的可见外部效果,但是我不知道在规范中的什么地方可以找到允许哪些优化的答案。

    1 回复  |  直到 6 年前
        1
  •  3
  •   Michael Kenzel    6 年前

    我认为,原则上,编译器将被允许以这样一种方式来优化它,即只捕获在as-if规则下使用的成员的一个副本。的相关部分 [expr.prim.lambda] §2 :

    []一个实现可以定义不同于下面描述的闭包类型,前提是这不会改变程序的可观察行为,而不是通过改变:

    • 闭合类型的尺寸和/或对准,
    • 闭包类型是否可以复制,或者
    • 闭包类型是否为标准布局类。

    但是,在快速测试中检查 sizeof() 在闭包类型中,没有一个主要编译器(clang、gcc、msvc)以这种方式优化闭包类型本身。

    不过,应该注意的是,只有当您将从lambda表达式获得的对象实际存储在某个地方(例如 std::function )通常情况下,lambda表达式的结果将被简单地用作某个函数模板的参数,然后丢弃。在这种情况下,当所有东西最终都是内联的时,优化器应该(并且在我的测试中)丢弃为复制从未被引用的数据而生成的代码。