捕获所有内容与仅捕获lambda中的某些内容

时间:2015-11-30 13:23:48

标签: c++ lambda c++14

我偶尔会想出这样的代码:

// lazy lambda:
[&] {
// use references to a, b, c
}

// pedantic lambda
[&a, &b, &c] {
// use references to a, b, c
}

我想知道根据C ++ 14标准(或更高版本)和编译器的实际经验,哪些lambda在性能和可执行大小方面更好。

3 个答案:

答案 0 :(得分:5)

这个例子没有区别。编译器只捕获lambda中显式引用的变量,使得延迟捕获等效于显式捕获。

懒惰捕捉稍微方便一些。更改lambda的主体以使用其他捕获的变量,或从现有lambda中删除对捕获的变量的所有引用,通常还需要更新显式捕获列表。通过延迟捕获,编译器将为您完成所有操作。

答案 1 :(得分:3)

除了你明确捕捉lambda中没有提到的东西的琐碎案例之外,还有两个可能存在差异的极端情况。

首先,隐式捕获通常不会捕获没有使用过的实体(但请参阅下一项的例外情况)。除其他外,这包括未评估的操作数中提到的内容,例如decltypesizeof的操作数,以及在某些上下文中使用时的某些constconstexpr局部变量(请参阅[basic.def.odr]了解完整的规则):

void f(int){}
void f2(const int &) {}
void t() {
    const int x = 8;
    constexpr double y = 8;
    const double z = 8; 
    auto g = []{ f(x); }; // OK
    auto g1 = [=]{ f(x); }; // OK, does not capture x

    // usually won't fire, though not guaranteed
    static_assert(sizeof(g) == sizeof(g1), "!!"); 

    auto g2 = []{ f(y); }; // OK
    auto g3 = []{ f(z); }; // Error, must capture z

    auto g4 = []{ f2(x); };  // Error, must capture x since it's odr-used
    auto g5 = [=]{ f2(x); }; // OK, captures x
    auto g6 = []{ f2(+x); }; // OK, doesn't odr-use x
    auto g7 = []{ f2(y); };  // OK
}

如果您手动编写捕获列表,则可能会捕获超出技术需要的内容,因为管理使用或不使用内容的规则非常复杂。

其次,通用lambda中的隐式捕获将捕获依赖表达式中使用的内容,即使它们不一定是使用过的,也是为了理智。借用an example from the standard

void f(int, const int (&)[2] = {})    { }   // #1
void f(const int&, const int (&)[1])  { }   // #2
void test() {
  const int x = 17;
  auto g2 = [=](auto a) {
    int selector[sizeof(a) == 1 ? 1 : 2]{};
    f(x, selector);  // OK: is a dependent expression, so captures x
                     // Will actually odr-use x only if sizeof(a) == 1
  };
}

但是,在编写通用lambda时,实际上并不需要捕获可能或者可能不会使用的东西;如果你实例化odr-使用该东西的函数调用操作符的特化,你只需要捕获它。因此,如果您从未以某种方式调用生成的通用lambda,那么隐式捕获可能会捕获超过您所需的最小值。例如,允许这样做:

void test() {
  const int x = 17;
  auto g3 = [](auto a) { // no capture
    int selector[sizeof(a) == 1 ? 1 : 2]{};
    f(x, selector);  // OK for now
  };
}

只要您不使用大小为1的任何内容调用g3g3(0)在典型系统上就可以了; g3('\0')不是。

答案 2 :(得分:1)

与上面提到的人一样,生成的代码没有区别。但是,我相信,最好是明确的 - 它提供更好的可读性(更容易推理变量生命周期)并且还为编译器提供更好的支持,当您访问变量时产生错误并不意味着

我相信'懒惰捕捉'根本不是一个好的功能。