Лямбда-захват и параметр с тем же именем - кто скрывает других? (лязг против gcc)

auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");
  • clang++ 3.6.0 и новее распечатайте "Вы используете clang++!" и предупредить о захвате foo быть неиспользованным

  • g++ 4.9.0 и новее распечатайте "Вы используете g ++!" и предупредить о параметре foo быть неиспользованным

Какой компилятор более точно следует стандарту C++ здесь?

пример wandbox

2 ответа

Решение

Обновление: как и было обещано председателем Core в нижней цитате, код теперь плохо сформирован:

Если идентификатор в простом захвате появляется как идентификатор объявления параметра предложения объявления лямбда-декларатора, то программа некорректна.


Было несколько проблем, связанных с поиском имен в лямбдах. Они были разрешены N2927:

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

Поиск всегда выполняется в контексте лямбда-выражения, а не "после" преобразования в тело функции-члена типа замыкания. См. [Expr.prim.lambda] / 8:

Составной оператор лямбда-выражения возвращает тело функции ([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 прав.

Если вы должны были сделать захват init-capture, то есть 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};
};

В любом случае, @nm правильно заметил, что нестатические члены-данные, объявленные для перехваченных копий переменных, на самом деле не называются. При этом к неназванному элементу данных по-прежнему осуществляется доступ с помощью идентификатора (то есть foo). Следовательно, имя параметра оператора вызова функции должно (позвольте мне сказать) скрывать этот идентификатор.
Как правильно указано @nm в комментариях к вопросу:

исходный захваченный объект [...] должен быть обычно затенен в соответствии с правилами области видимости

Из-за этого я бы сказал, что лязг прав.

Другие вопросы по тегам