Подтвердить что ты не робот

Захват статического лямбда в вложенной лямбда

В этот ответ Я использую этот код:

std::vector<std::vector<int>> imat(3, std::vector<int>(10));

std::for_each(imat.begin(), imat.end(), [&](auto& i) {
    static auto row = 0;
    auto column = 0;
    std::transform(i.begin(), i.end(), i.begin(), 
        [&](const auto& /*j*/) {
            return row * column++; 
    }); 

    ++row; 
});

Но я замечаю некоторое неправильное поведение при захвате static auto row в зависимости от компилятора.

Clang 3.7.0 дает:

0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9
0 2 4 6 8 10 12 14 16 18

gcc 5.1.0 дает:

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

И Visual Studio 2015 дает мне ошибку времени компиляции:

В компиляторе произошла внутренняя ошибка.

Если я изменяю захват вложенного захвата на захват row явно, я получаю ошибку компилятора:

идентификатор в захвате должен быть переменной с автоматическим временем хранения, объявленным в области охвата лямбда

Мне удалось захватить static во вложенной лямбда? Это кажется законным, но есть так много проблем!

EDIT:

Фози отметил, что я могу заставить Visual Studio 2015 скомпилировать и дать тот же результат, что и Clang 3.7.0, если я изменю тип вложенного лямбда-параметра от const auto& до const int&. Который кажется совершенно не связанным, но он работает.

Это не работает, если я попытаюсь зафиксировать row явно. В этом случае я все еще получаю ошибку компилятора:

идентификатор в захвате должен быть переменной с автоматическим временем хранения, объявленным в области охвата лямбда

Я сообщил об ошибке Visual Studio 2015 здесь: https://connect.microsoft.com/VisualStudio/feedback/details/1930409/capturing-a-lambdas-static-in-a-nested-lambda

4b9b3361

Ответ 1

Внутренняя ошибка компилятора (ICE) всегда является ошибкой.

Нам не нужно фиксировать переменные статической продолжительности хранения, но нам нужно захватить автоматические переменные odr-used. Из проекта стандартного раздела С++ 5.1.2:

Компонентный оператор лямбда-выражений дает функциональное тело (8.4) оператора вызова функции, , но для целей поиска по имени (3.4), определяя тип и значение этого (9.3.2) и преобразуя идекспрессионы ссылаясь на нестатические члены класса на выражения доступа к членам класса, используя (* this) (9.3.1), составной оператор рассматривается в контексте лямбда-выражения.

поэтому row должно быть видимым во внутренней лямбда и:

[...] Если лямбда-выражение или экземпляр шаблона оператора вызова функции общего лямбда-odr-uses (3.2) это или переменная с автоматическим временем хранения от ее охвата, эта организация должна быть захвачена лямбда-выражение. [...]

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

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

Как для Visual Studio, так и для gcc для соответствия результатам clang, я могу перемещать row в глобальное пространство имен, видеть его вживую для gcc. Также, как указывает Фози, изменение const auto& /*j*/ на const int& /*j*/ заставляет его работать.

Похоже, что gcc принимает явный захват неавтоматических переменных в качестве расширения, и даже тогда явное захват row, например [&, &row](const auto & ) все равно производит все нули.

Далее для gcc, если я переместил определение для row в main, то я вижу следующую ошибку (видеть ее в прямом эфире):

/tmp/cchzwtQI.s: Assembler messages:
/tmp/cchzwtQI.s:1572: Error: symbol `_ZL3row' is already defined

Который кажется мне ошибкой компилятора.

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