Этот баг MS VC++ 2010 не очевидный и очень тонкий в контексте его определения и объяснения того, что он действительно является багом. Но лично я, следуя тому, что написано в стандарте С++ 2011, считаю это багом.
Приведу сразу же пример кода, воспроизводящий этот баг, а затем постараюсь объяснить, почему это именно баг.
#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <numeric>
#include <iterator>
void f()
{
const int N = 5;
auto m1 = []
{
int a[N];
std::iota( a, a + N, 0 );
std::copy( a, a + N, std::ostream_iterator<int>( std::cout, " " ) );
std::cout << std::endl;
};
}
int _tmain(int argc, _TCHAR* argv[])
{
f();
return 0;
}
В этом примере кода красным цветом выделена конструкция
[], которая следует после выражения
auto m1 =. Пока не буду останавливаться на объяснении значения этой выделенной конструкции, покажу лишь, какое сообщение об ошибке выдает компилятор MS VC++ 2010.
error C3493: "N" нельзя передать неявно, поскольку не задан режим передачи по умолчанию Это сообщение об ошибке означает, что лямбда выражение не может использовать имя целочисленной константы
N, если это имя не захвачено лямбда выражением. Как раз конструкция
[] с пустым содержимом между квадратными скобками и означает, что ни одно имя из достижимой области видимости лямбда выражение не захватило (capture).
Причем сообщение об ошибке указывает на строку, где объявляется массив в лямбда выражении:
int a[N];.
Теперь еще более интересно. Если подвести курсор к этому определению массива, то компилятор MS VC++ 2010 не высвечивает подсказку, что в этой строке есть ошибка. Он красной подсветкой высвечивает лишь строки с вызовом стандартных алгоритмов
std::iota и
std::copy, так как не может понять, что означает имя
N в выражении
a + N, которое указано в качестве аргументов для этих вызовов алгоритмов.
Теперь изменим пример и, послушав компилятор, зададим режим передачи локальных имен функции в лямбда выражение по умолчанию.
#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <numeric>
#include <iterator>
void f()
{
const int N = 5;
auto m1 = [=]
{
int a[N];
std::iota( a, a + N, 0 );
std::copy( a, a + N, std::ostream_iterator<int>( std::cout, " " ) );
std::cout << std::endl;
};
}
int _tmain(int argc, _TCHAR* argv[])
{
f();
return 0;
}
Обратите внимание, что сейчас записано
[=], то есть внутри квадратных скобок задан режим по умолчанию передачи переменных в лямбда выражение из достижимой области видимости пп значению.
Теперь код компилируется MS VC++ 2010?
Ничего подобного! Выдается уже другое сообщение об ошибке.
error C2057: требуется константное выражение и указывается та же строка исходного текста, где определяется массив
int a[N];. То есть компилятор не захватывает переменную
N, как константу. И поэтому, как не старайся, даннный код нельзя скомпилировать с помощью MS VC++ 2010.
А должен ли он компилироваться? Да должен! И причем, как я считаю, в обоих случаях.
Если эти два примера запустить с помощью
онлайнового компилятора (возможно, там еще сохранился набранный мною код примера), то все успешно компилируется.
Почему это баг именно компилятора MS VC++ 2010, а не некорректная работа онлайнового компилятора, который не должен был компилировать этот код.
Дело в том, что константа
N вычисляется в выражениях еще на этапе компиляции. Ее адрес нигде не используется, а потому нет необходимости ее захватывать в лямбда выражении. Компилятор просто должен был на этапе компиляции вместо
N поставить значение 5 и успешно скомпилировать код. Но MS VC++ 2010 этого не делает.
Шлем сообщение о баге в Майкрософт и ждем, что они ответят.