auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");
лязг ++ 3.6.0 и новее распечатать «Вы используете Clang ++!» и предупредить о захватить foo
быть неиспользованным
g ++ 4.9.0 и новее распечатать «Вы используете g ++!» и предупредить о параметр foo
быть неиспользованным
Какой компилятор более точно следует стандарту C ++ здесь?
Обновление: как и обещал председатель Core в нижней цитате, код сейчас плохо сформирован:
Если идентификатор в простой захват появляется как описатель-идентификатор параметра лямбда-описатель«s Параметр декларирование придаточного, программа плохо сформирована.
Было несколько проблем, связанных с поиском имен в лямбдах. Они были решены N2927:
Новая формулировка больше не опирается на поиск, чтобы переназначить использование захваченных объектов. Это понятнее
отрицает интерпретации, которые лямбда компаунд-заявление обрабатывается в два прохода или что любые имена в этом компаунд-заявление может разрешить члену типа замыкания.
Поиск всегда выполняется в контексте лямбда-выражение, никогда «после» преобразования в тело функции-члена типа замыкания. Увидеть [Expr.prim.lambda] / 8:
лямбда-выражение«s компаунд-заявление дает функция тела ([dcl.fct.def]) оператора вызова функции, но для целей поиска имени, […], компаунд-заявление рассматривается в контексте лямбда-выражение. [ пример:
struct S1 { int x, y; int operator()(int); void f() { [=]()->int { return operator()(this->x+y); // equivalent to: S1::operator()(this->x+(*this).y) // and this has type S1* }; } };
—конец примера ]
(В этом примере также ясно, что поиск каким-то образом не учитывает сгенерированный элемент захвата типа замыкания.)
Имя foo
не (повторно) объявлен в захвате; он объявляется в блоке, содержащем лямбда-выражение. Параметр foo
объявлен в блоке, который вложен в этот внешний блок (см. [Basic.scope.block] / 2, который также явно упоминает параметры лямбда). Порядок поиска четко от внутренних до внешних блоков. Следовательно, параметр должен быть выбран, то есть Clang прав.
Если вы хотите сделать захват инициализацией, то есть foo = ""
вместо foo
ответ не будет понятен. Это потому, что захват сейчас на самом деле вызывает декларацию чей «блок» не дан. Я связался с председателем по этому вопросу, который ответил
Это проблема 2211 (новый список проблем появится на сайте open-std.org в ближайшее время, к сожалению, только с местозаполнителями для ряда проблем, из которых это одна; я прилагаю все усилия, чтобы заполнить эти пробелы перед Kona встреча в конце месяца). CWG обсуждала это во время нашей январской телеконференции, и направление состоит в том, чтобы сделать программу плохо сформированной, если имя записи также является именем параметра.
Я пытаюсь собрать несколько комментариев к вопросу, чтобы дать вам значимый ответ.
Прежде всего, обратите внимание, что:
foo
Поэтому логика заставит меня на первый взгляд сказать, что параметр должен скрывать захваченную переменную, как если бы:
struct Lambda {
template<typename T> void operator()(T foo) const { /* ... */ }
private: decltype(outer_foo) foo{outer_foo};
};
Во всяком случае, @ n.m. правильно заметил, что нестатические члены-данные, объявленные для перехваченных копий переменных, на самом деле не названы. При этом к неназванному элементу данных по-прежнему осуществляется доступ с помощью идентификатора (то есть foo
). Поэтому имя параметра оператора вызова функции все же должно быть (позвольте мне сказать) затенить этот идентификатор.
Как правильно указано @ n.m. в комментариях к вопросу:
исходный захваченный объект […] должен быть обычно затенен в соответствии с правилами области видимости
Из-за этого я бы сказал, что лязг прав.