Lambda捕获为const引用?

Joh*_*ing 151 c++ lambda c++11 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;
}
Run Code Online (Sandbox Code Playgroud)

更新:由于这是一个老问题,如果C++ 14中有设施来帮助解决这个问题,那么更新它可能会更好.C++ 14中的扩展是否允许我们通过const引用捕获非const对象?(2015年8月)

Ste*_*e M 115

const 从n3092起,它不属于捕获的语法:

capture:
  identifier
  & identifier
  this
Run Code Online (Sandbox Code Playgroud)

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

感觉像对我的监督,但我没有非常密切地遵循标准化过程.

  • 我只是追踪一个错误,回到一个变量,该变量是从可变的捕获中修改的,但应该是``const``.或者更准确地说,如果捕获变量是``const``,编译器就会在程序员身上强制执行正确的行为.如果语法支持``[&mutableVar,const&constVar]``,那就太好了. (42认同)
  • Constness继承自捕获的变量.因此,如果你想把`a`捕获为`const`,在lambda之前声明`const auto&b = a;`并捕获`b` (32认同)
  • @StenSoft Bleargh.除了显然这不适用于通过引用捕获成员变量:`[&foo = this-> foo]``const`函数内部给出了一个错误,指出*capture本身*丢弃限定符.不过,我想这可能是GCC 5.1中的一个错误. (5认同)

Pio*_*cki 91

C++ 14:

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

DEMO


C++ 17:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};
Run Code Online (Sandbox Code Playgroud)

演示2

  • @MM`&basic_string = std :: as_const(best_string)`应解决所有问题 (23认同)
  • @AaronMcDaid`const_cast`可以无条件改变挥发性对象const对象(当询问投射到`const`),从而,用于将约束我更喜欢`static_cast` (12认同)
  • @PiotrSkotnicki除非这是一种可怕的方式来编写“应该”像const&best_string这样简单的东西,否则这个问题不存在。 (11认同)
  • 我想这是使用`const_cast`的好时机? (5认同)

zhb*_*zhb 12

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

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

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

lambda函数是const(不能更改其范围内的值),因此当您按值捕获变量时,该变量不能更改,但该引用不在lambda范围内.

  • 如果您需要在包含范围内修改`better_string`,则此解决方案将不起作用。捕获为const-ref的用例是当变量需要在包含范围内而不是在lambda内可变时。 (2认同)

Kla*_*aim 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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • "如果仿函数应该与上下文无关,那就把它变成一个真正的函子"......并亲吻可能的内联再见? (3认同)

小智 6

有一个更短的方法。

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

它将是“const std::reference_wrapper<< T >>”类型。

[best_string = cref(best_string)](const string& s)
{
    best_string = s; // fails
};
Run Code Online (Sandbox Code Playgroud)

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


Ale*_*lex 5

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

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

使用副本

有关复制捕获的lambda的有趣部分是那些实际上是只读的,因此完全按照你的意愿行事.

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}
Run Code Online (Sandbox Code Playgroud)

使用std :: bind

std::bind减少函数的优点.但请注意,这可能会导致通过函数指针进行间接函数调用.

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}
Run Code Online (Sandbox Code Playgroud)

  • 除非对包含范围内的变量进行更改,否则不会反映在 lambda 中。它不是引用,它只是一个不应重新分配的变量,因为重新分配并不意味着它看起来的含义。 (2认同)