Lambda捕获为const引用?

时间:2010-09-22 19:23:37

标签: c++ c++11 lambda c++14

是否可以通过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";

    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引用捕获非const对象? ( 2015年8月

8 个答案:

答案 0 :(得分:107)

截至n3092,

const不在捕获的语法中:

capture:
  identifier
  & identifier
  this

该文本仅提及逐个复制和按引用捕获,并未提及任何类型的常量。

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

答案 1 :(得分:79)

C ++ 14:

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

DEMO

C ++ 17:

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

DEMO 2

答案 2 :(得分:13)

我认为捕获部分不应指定const,因为捕获意味着它只需要一种方法来访问外部范围变量。

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

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

lambda函数 是const(不能更改其范围内的值),因此当您按值捕获变量时,该变量无法更改,但参考不属于lambda范围。

答案 3 :(得分:8)

我想如果你没有将变量用作仿函数的参数,那么你应该使用当前函数的访问级别。如果你认为你不应该,那么将你的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一样。

答案 4 :(得分:5)

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

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

使用副本

关于带有副本捕获的lambdas的有趣部分是那些实际上是只读的,因此完全按照你的意愿行事。

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);
}

答案 5 :(得分:3)

有一条较短的路。

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

它将是“const std :: reference_wrapper&lt;&lt; T&gt;&gt;”类型。

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

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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