std ::list::remove_if сходит с ума, если в сочетании с общим лямбда

#gcc #lambda #clang #c 14 #language-lawyer

#gcc #лямбда #лязг #c 14 #язык-юрист

Вопрос:

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

Рассмотрим приведенный ниже код:

 #include<algorithm>
#include<list>

template<typename U>
struct S {
    using FT = void(*)(); 
    struct T { FT func; };

    template<typename> 
    static void f() { } 

    std::list<T> l{ { amp;f<int> }, { amp;f<char> } };

    void run() {  
        l.remove_if([](const T amp;t) { return t.func == amp;f<int>; }); // (1)
        l.remove_if([](const auto amp;t) { return t.func == amp;f<int>; }); // (2)
    }
};

int main() {
    S<void> s;
    s.run();
}
  

clang v3.9 компилирует оба (1) и (2), как и ожидалось.
GCC v6.2 компилирует (1), но не компилирует (2) .
Возвращаемая ошибка:

ошибка: ‘f’ не был объявлен в этой области

Более того, обратите внимание, что GCC компилирует (2), если он изменен следующим образом:

 l.remove_if([](const auto amp;t) { return t.func == amp;S<U>::f<int>; }); // (2)
  

Насколько я знаю, использование const auto amp; вместо const T amp; не должно изменять поведение в этом случае.

Это ошибка GCC?

Комментарии:

1. Мне интересно, в какой области компилятор генерирует класс, представляющий общий лямбда: file-scope, class-scope, function-scope (как локальный класс, но который не поддерживает шаблон функции в качестве члена)?

2. Отсюда тип закрытия объявляется в наименьшей области блока, области класса или области пространства имен, которая содержит соответствующее лямбда-выражение . Я бы сказал S<U> , вот почему я думаю, что это ошибка, и она должна видеть объявление f .

3. @Nawaz Может быть, это даже более уместно — область охвата локального лямбда-выражения — это набор охватывающих областей вплоть до самой внутренней охватывающей функции и ее параметров ?

4. @skypjack хм, если вы удалите внешний шаблон ( typename <class U> ), он также будет скомпилирован…

5. @W.F. Да, конечно, я это заметил. Минимальный, рабочий пример действительно минимальный !! Я сделал все возможное. 😉

Ответ №1:

Согласно [expr.prim.lambda]:

8 — […] [Для] целей поиска имени (3.4) […] составное выражение рассматривается в контексте лямбда-выражения. […]

MCVE:

 template<int>
struct S {
  template<int> static void f();
  S() { void(*g)(char) = [](auto) { f<0>; }; }
};
S<0> s;
  

Добавление составного оператора к контексту лямбда-выражения дает четко действительную программу:

 template<int>
struct S {
  template<int> static void f();
  S() { f<0>; }
};
S<0> s;
  

Так что да, это ошибка в gcc.