Лямбда-захват как постоянная ссылка? - PullRequest
143 голосов
/ 22 сентября 2010

Можно ли захватить по константной ссылке в лямбда-выражении?

Я хочу, чтобы назначение, отмеченное ниже, не выполнялось, например:

#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 захватывать неконстантный объект по константной ссылке?( август 2015 )

Ответы [ 8 ]

108 голосов
/ 22 сентября 2010

const отсутствует в грамматике для захватов по состоянию на n3092:

capture:
  identifier
  & identifier
  this

В тексте упоминается только захват за копией и захват по ссылке, и не упоминается никакой константы.

Для меня это похоже на недосмотр, но я не очень внимательно следил за процессом стандартизации.

81 голосов
/ 07 сентября 2015

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

ДЕМО 2

13 голосов
/ 04 января 2014

Я думаю, что часть захвата не должна указывать const, так как захват означает, что ей нужен только способ доступа к переменной внешней области видимости.

Спецификатор лучше указывать во внешней области видимости.

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

лямбда-функция является константой (не может изменить значение в своей области видимости), поэтому при захвате переменной по значению переменная не может быть изменена, но ссылка не является в области лямбды.

8 голосов
/ 23 сентября 2010

Полагаю, если вы не используете переменную в качестве параметра функтора, вам следует использовать уровень доступа текущей функции. Если вы считаете, что этого не следует делать, то отделите свою лямбду от этой функции, она не является ее частью.

В любом случае, вы можете легко достичь того же, чего хотите, используя вместо этого другую ссылку на констант:

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

Но это то же самое, что предположить, что ваша лямбда должна быть изолирована от текущей функции, что делает ее не лямбда.

5 голосов
/ 12 декабря 2013

Я думаю, что у вас есть три разных варианта:

  • не используйте константную ссылку, но используйте захват копии
  • игнорируйте тот факт, что он может быть изменен
  • используйте std :: bind для привязки одного аргумента двоичной функции, которая имеет ссылку на константу.

с использованием копии

Интересная часть о лямбдах с захватами копий заключается в том,на самом деле только для чтения и, следовательно, делать именно то, что вы хотите, чтобы они.

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

с использованием std :: bind

std::bind уменьшает арность функции.Однако обратите внимание, что это может / приведет к косвенному вызову функции через указатель функции.

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}
3 голосов
/ 13 сентября 2017

Есть более короткий путь.

Обратите внимание, что перед "best_string" нет амперсанда.

Это будет тип "const std :: reference_wrapper << T >>".

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

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

0 голосов
/ 02 февраля 2018

Использование const просто приведет к тому, что алгоритм ampersand установит строку в исходное значение. Другими словами, лямбда не будет определяться как параметр функции, хотя окружающая область будет иметь дополнительную переменную ...Однако без ее определения строка не будет определяться как типичная [&, & best_string] (строковые константы) Поэтому , скорее всего, лучше, если мы просто оставим ее при этом, пытаясь захватить ссылку.

0 голосов
/ 16 ноября 2016

Используйте clang или дождитесь исправления этой ошибки gcc: ошибка 70385: ошибка лямбда-захвата по ссылке на константную ссылку [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385]

...