【问题标题】:What does it mean for a lambda to be static?lambda 是静态的意味着什么?
【发布时间】:2013-10-15 08:10:35
【问题描述】:

假设我有一个二分搜索函数,它初始化并使用一个 lambda:

bool custom_binary_search(std::vector<int> const& search_me)
{
  auto comp = [](int const a, int const b)
    {
      return a < b;
    };

  return std::binary_search(search_me.begin(), search_me.end(), comp);
}

没有指出这完全是多余的,只关注 lambda;每次都声明和定义该 lambda 对象是否昂贵?它应该是静态的吗? lambda 是静态的意味着什么?

【问题讨论】:

  • 您不能将 lambda 表达式 声明为静态。您只能将 变量 comp 声明为静态。在这种情况下,它与任何变量的含义相同。
  • @KerrekSB 我不明白;如果我将 lambda 对象声明为静态,这会改变什么吗?该函数还能像以前一样采用本地参数吗?它会减少每次我的函数运行时重新声明变量的需要吗?
  • 正如我所说,你只能声明变量为静态,即static auto comp = ...。然后当控制流第一次通过声明时,它将被精确地初始化一次。由于 lambda 是无状态的,所以它根本不应该有任何区别。即使是现在的编写方式,它也可能不会生成任何代码。
  • 人们通常不是“每次都声明和定义一个 lambda 对象”,而是将其设为一个简单的非成员函数 (free function) 并将其与算法一起使用。这也应该解决在头文件中只声明一次并在某些代码文件中定义一次的问题。

标签: c++ lambda


【解决方案1】:

类型为 的变量 'comp' 可以设为静态,几乎与任何其他局部变量一样,即每次运行此函数时,它都是同一个变量,指向同一个内存地址) .

但是,请注意使用闭包,这会导致细微的错误(按值传递)或运行时错误(按引用传递),因为闭包对象也只初始化一次:

bool const custom_binary_search(std::vector<int> const& search_me, int search_value, int max)
{
  static auto comp_only_initialized_the_first_time = [max](int const a, int const b)
  {
      return a < b && b < max;
  };

  auto max2 = max;
  static auto comp_error_after_first_time = [&max2](int const a, int const b)
  {
      return a < b && b < max2;
  };

  bool incorrectAfterFirstCall = std::binary_search(std::begin(search_me), std::end(search_me), search_value, comp_only_initialized_the_first_time);
  bool errorAfterFirstCall = std::binary_search(std::begin(search_me), std::end(search_me), search_value, comp_error_after_first_time);

  return false; // does it really matter at this point ?
}

请注意,“max”参数只是用来引入您可能希望在比较器中捕获的变量,而“custom_binary_search”实现的功能可能不是很有用。

【讨论】:

  • 这可能吗?我会假设在第一次调用函数之前初始化静态局部变量。因此,我假设静态 lambda 变量不能使用捕获。
  • (我编辑了几个错别字的答案,例如( -> {。)
  • @AaronMcMaid static local 是if (global_var_initialized) global_var = ...) 的语法糖。所以它在第一次调用时被初始化。我还认为在静态 lambda 中捕获是一个坏主意。 max 将保留第一次调用的值。
  • 感谢编辑 - 为了清晰起见,我重新编辑以使搜索值成为顶级函数的参数
【解决方案2】:

以下代码在 Visual Studio 2013 中编译并运行正常:

bool const test(int & value)
{
    //edit `&value` into `&` @log0
    static auto comp = [&](int const a, int const b)
    {
        return a < (b + value);
    };

    return comp(2,1);
}

后来:

int q = 1;
cout << test(q); // prints 0 //OK
q++;
cout << test(q); // prints 1 //OK

编译器会将任何 lambda 声明转换为常规函数,这是在编译时完成的。 test 函数中的实际定义只是对带有指向 c 函数的指针的 comp 变量的常规赋值。 闭包通常是相同的,但只有在它们定义的范围内才能正常工作。在任何其他范围内,它们都会失败或产生内存损坏错误。

定义comp static 只会对性能提升微不足道或根本没有。

希望这会有所帮助: 拉兹万。

【讨论】:

  • @log0,在这个特定的代码 sn-p 中,我认为实际上定义了行为。被持有的引用是对q 的引用,并且该变量通过对test(q) 的两次调用保持在范围内。这是有效的,因为在两个地方都使用了引用:将 arg 带到普通函数,并在 lambda 中捕获它。如果其中任何一个&amp; 丢失,就会出现问题。但就目前而言,它是定义的行为。 (但这是一个奇怪的程序!)
  • @Raxvan 我也做同样的事情,不是吗?您通过 ref 捕获所有内容,但仅使用闭包中的 value,因此 value 像以前一样由 ref 获取。
  • @AaronMcDaid 这对我来说似乎很冒险。 lambda 捕获对 ref 的引用,因此它可以工作,假设 ref value 将始终存储在同一地址,而不管提供给 test 的参数的地址如何。但是,如果编译器进行任何优化,例如直接将q 的地址提供给 lambda,那么在下次调用另一个参数时它将失败(如果幸运的话)。我认为在静态 lambda 中捕获通常是不安全的。
  • @log0,我认为我们的问题可以简化为不涉及 lambda。问题是:什么是 'ref to a ref?'。考虑一下:void f(int &amp;i) { i++; }; void g(int &amp;j) { f(j); };,然后我们运行:int main() { int q = 3; g(q); cout &lt;&lt; q; } 。这将打印 4,因为对 q 的引用在两个函数调用中仍然存在 - 在 f 内,它仍然是对 q 的引用。如果相反,g 是 void g(int j),那么只有 g 中的局部变量会发生变化。如果您同意,并且是完全标准定义的行为,那么我们可以想象一个对象持有该引用的时间更长。
  • @AaronMcDaid 在您的示例中没有问题,因为qg 的生命周期内存在,而jf 的生命周期内存在。所以if 的整个执行过程中总是引用一个有效的变量。一旦g 返回,int &amp; j 就不再存在,所以任何指向j 的引用在g 返回后都不再有效。 @Raxvan 所做的是在 g 返回后保留对 j 的引用。
猜你喜欢
  • 2016-12-18
  • 2023-04-02
  • 1970-01-01
  • 2015-07-31
  • 2017-03-09
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 2017-09-01
相关资源
最近更新 更多