200

const是否可以在 lambda 表达式中通过引用捕获?

我希望下面标记的作业失败,例如:

#include <algorithm>
#include <string>

using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";

    for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
      {
        best_string = s; // this should fail
      }
    );
return 0;
}

更新:由于这是一个老问题,如果 C++14 中有工具可以帮助解决这个问题,更新它可能会很好。C++14 中的扩展是否允许我们通过 const 引用捕获非常量对象?(2015 年 8 月

4

8 回答 8

164

中使用static_cast/ const_cast

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

演示


中使用std::as_const

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

演示 2

于 2015-09-07T13:56:35.983 回答
136

const不在 n3092 的捕获语法中:

capture:
  identifier
  & identifier
  this

文本只提到了按拷贝捕获和按引用捕获,并没有提到任何类型的 const-ness。

对我来说感觉像是一个疏忽,但我并没有非常密切地关注标准化过程。

于 2010-09-22T19:40:16.753 回答
16

我认为捕获部分不应该指定const,作为捕获手段,它只需要一种访问外部范围变量的方法。

说明符最好在外部范围中指定。

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

lambda 函数是 const(can't change value in its scope),所以当你按值捕获变量时,变量不能改变,但引用不在 lambda 范围内。

于 2014-01-04T03:20:28.390 回答
9

我想如果你不使用变量作为函子的参数,那么你应该使用当前函数的访问级别。如果您认为不应该这样做,请将您的 lambda 与此函数分开,它不是其中的一部分。

无论如何,您可以通过使用另一个 const 引用轻松实现您想要的相同功能:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

但这与假设您的 lambda 必须与当前函数隔离,使其成为非 lambda 相同。

于 2010-09-22T21:23:14.247 回答
9

有一个更短的方法。

请注意,“best_string”之前没有与号。

它将是一种const std::reference_wrapper<T>类型。

[best_string = std::cref(best_string)](const string& s)
{
    best_string = s; // fails
};

http://coliru.stacked-crooked.com/a/0e54d6f9441e6867

于 2017-09-13T16:52:28.103 回答
5

我认为您有三种不同的选择:

  • 不要使用 const 引用,而是使用复制捕获
  • 忽略它是可修改的事实
  • 使用 std::bind 绑定具有 const 引用的二进制函数的一个参数。

使用副本

关于带有复制捕获的 lambda 的有趣部分是它们实际上是只读的,因此完全按照您的意愿执行。

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

使用 std::bind

std::bind减少函数的arity。但是请注意,这可能/将导致通过函数指针进行间接函数调用。

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}
于 2013-12-11T20:19:37.583 回答
0

使用 clang 或等到此 gcc 错误修复:错误 70385:通过 const 引用的引用捕获 Lambda 失败 [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]

于 2016-11-16T16:33:44.807 回答
-2

使用 const 只会让算法将字符串设置为原始值,换句话说,lambda 不会真正将自己定义为函数的参数,尽管周围的范围会有一个额外的变量......没有定义它但是,它不会将字符串定义为典型的 [&, &best_string](string const s) 因此,如果我们将其保留在那里,尝试捕获引用,它很可能会更好。

于 2018-02-02T20:24:32.833 回答