Вывод типа для переменной, объявленной в захвате лямбды - PullRequest
0 голосов
/ 20 февраля 2020

В этом фрагменте я объявляю переменную i внутри лямбда-захвата.

int main()
{
    vector<int> vec;

    // Pushing 0-99 numbers to vector
    std::generate_n(std::back_inserter(vec), 100, [i = 0]() mutable { return i++; });

    return 0;
}

Как видно, нигде не указано типа для i.

Насколько я знаю, я могу написать эквивалентную функциональность следующим образом:

int main()
{
    vector<int> vec;

    // Pushing 0-99 numbers to vector
    std::generate_n(std::back_inserter(vec), 100, [](){
             static int i = 0; 
             return i++; });

    return 0;
}

Как компилятор узнает тип i в первом примере? Выведено ли оно из операции, которую я выполняю на нем (++)? Знает ли он, что это int из-за контейнера?

Нет никаких претензий при компиляции с G CC с -std = c ++ 14 и -std = c ++ 17. Тем не менее, если я скомпилирую с -std = c ++ 11, я получу следующее предупреждение:

lambda_test.cpp: In function ‘int main()’:
lambda_test.cpp:24:51: warning: lambda capture initializers only available with -std=c++14 or -std=gnu++14
  std::generate_n(std::back_inserter(first), 100, [i = 0]() mutable { return i++; });
                                                   ^

MORE : учитывая комментарии, я попытался увидеть разницу в том, что компилятор производит для c ++ 11 и 14, но генерирует тот же код: https://cppinsights.io/s/43411e6f

Ответы [ 2 ]

2 голосов
/ 20 февраля 2020

Как говорит ошибка, вы должны активировать C ++ 14, чтобы инициализатор лямбда-захвата работал, так как он был добавлен в C ++ 14.

Насколько я знаю, я могу написать эквивалентная функциональность в этом смысле:

Нет, stati c хранилище функционально отличается. С помощью захвата вы можете скопировать лямбду, и она скопирует состояние снимков. С переменными stati c каждая лямбда-функция обращается к одному и тому же глобальному элементу.

Как компилятор узнает тип i в первом примере? Выводится ли он из выполняемой мной операции на это (++)? Знает ли он, что это int из-за контейнера?

Нет, поскольку многие типы имеют оператор ++.

Компилятор просто использует инициализатор для вывода типа. Вы можете видеть это, как будто там был скрыт auto:

std::generate_n(std::back_inserter(vec), 100, [/* auto */ i = 0]() mutable { return i++; });

Литерал 0 имеет тип int. Так что i - это int.

Технически это тоже можно сделать:

std::generate_n(std::back_inserter(vec), 100, [/* auto */ i = 0ull]() mutable { return i++; });

Тогда i имеет тип unsigned long long, так как литерал 0ull равен этого типа.

1 голос
/ 20 февраля 2020

Он работает так же, как и при

auto i = 0;

Вы должны указать инициализатор, если вы не захватываете существующую переменную, и этот инициализатор используется для определения типа.

...