Форум » C/C++ » MS VC++ 2010 баг при использовании константной переменной в лямбда выражении. » Ответить

MS VC++ 2010 баг при использовании константной переменной в лямбда выражении.

Сыроежка: Этот баг MS VC++ 2010 не очевидный и очень тонкий в контексте его определения и объяснения того, что он действительно является багом. Но лично я, следуя тому, что написано в стандарте С++ 2011, считаю это багом. Приведу сразу же пример кода, воспроизводящий этот баг, а затем постараюсь объяснить, почему это именно баг. [pre2]#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; }[/pre2] В этом примере кода красным цветом выделена конструкция [], которая следует после выражения auto m1 =. Пока не буду останавливаться на объяснении значения этой выделенной конструкции, покажу лишь, какое сообщение об ошибке выдает компилятор MS VC++ 2010. error C3493: "N" нельзя передать неявно, поскольку не задан режим передачи по умолчанию Это сообщение об ошибке означает, что лямбда выражение не может использовать имя целочисленной константы N, если это имя не захвачено лямбда выражением. Как раз конструкция [] с пустым содержимом между квадратными скобками и означает, что ни одно имя из достижимой области видимости лямбда выражение не захватило (capture). Причем сообщение об ошибке указывает на строку, где объявляется массив в лямбда выражении: int a[N];. Теперь еще более интересно. Если подвести курсор к этому определению массива, то компилятор MS VC++ 2010 не высвечивает подсказку, что в этой строке есть ошибка. Он красной подсветкой высвечивает лишь строки с вызовом стандартных алгоритмов std::iota и std::copy, так как не может понять, что означает имя N в выражении a + N, которое указано в качестве аргументов для этих вызовов алгоритмов. Теперь изменим пример и, послушав компилятор, зададим режим передачи локальных имен функции в лямбда выражение по умолчанию. [pre2]#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; }[/pre2] Обратите внимание, что сейчас записано [=], то есть внутри квадратных скобок задан режим по умолчанию передачи переменных в лямбда выражение из достижимой области видимости пп значению. Теперь код компилируется MS VC++ 2010? Ничего подобного! Выдается уже другое сообщение об ошибке. error C2057: требуется константное выражение и указывается та же строка исходного текста, где определяется массив int a[N];. То есть компилятор не захватывает переменную N, как константу. И поэтому, как не старайся, даннный код нельзя скомпилировать с помощью MS VC++ 2010. А должен ли он компилироваться? Да должен! И причем, как я считаю, в обоих случаях. Если эти два примера запустить с помощью онлайнового компилятора (возможно, там еще сохранился набранный мною код примера), то все успешно компилируется. Почему это баг именно компилятора MS VC++ 2010, а не некорректная работа онлайнового компилятора, который не должен был компилировать этот код. Дело в том, что константа N вычисляется в выражениях еще на этапе компиляции. Ее адрес нигде не используется, а потому нет необходимости ее захватывать в лямбда выражении. Компилятор просто должен был на этапе компиляции вместо N поставить значение 5 и успешно скомпилировать код. Но MS VC++ 2010 этого не делает. Шлем сообщение о баге в Майкрософт и ждем, что они ответят.

Ответов - 3

Сыроежка: Поступил ответ от Майкрософт о вышеописанном баге. Похоже, что команда разработчиков Майкрософт не вникала в суть проблемы, так как их ответ является на мой взгляд некорректным с точки зрения предложенного Майкрософт обходного пути избежать ошибки. Вот текст ответа: Posted by Microsoft on 2/25/2012 at 9:39 PM Hello, Thank you for your comment. You'll need to capture the const qualified variable from enclosing scope in order to reference it from lambda. #include <iostream> #include <algorithm> #include <numeric> #include <iterator> #include <tchar.h> void f() { const int N = 5; auto m1 = [N]() { 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; } Thanks, Ulzii Luvsanbat То есть все свелось к тому, что предлагается передавать константу из внешнего блока (тела функции) в лямбда выражение явно, то есть использовать конструкцию auto m1 = [N]() Я естественно проверил этот код, и если я что-то не так сделал, в чем я очень сомневаюсь, то этот код по-прежнему не компилируется компилятором MS VC++ 2010. Выдается уже знакомое сообщение об ошибке error C2057: требуется константное выражение Поэтому с моей стороны последовало ответное сообщение, что ничего не изменилось: Posted by Сыроежка on 2/26/2012 at 1:00 PM Ulzii, I tested the code you showed and I got the same error as I early reported, that is, error C2057: требуется константное выражение (a constant expression is required). Nothing is changed. Надеюсь, на этот раз в команде разработчиков Майкрософт отнесутся более внимательно к указанному мною багу.

Сыроежка: Как оказывается, природа этого бага более общая и имеет отношению к любому локальному объявлению класса в MS VC++ 2010. Локальные классы в MS VC++ 2010 вопреки положениям стандарта С++ 2011 не могут обращаться к константным переменным с автоматической памятью. Это можно продемонстрировать следующими двумя примерами. [pre2]#include "stdafx.h" #include <iostream> int _tmain(int argc, _TCHAR* argv[]) { static int x = 10; const int y = 20; struct A { int h() { return ( x + y ); } }; A a; std::cout << "a.h() = " << a.h() << std::endl; return ( 0 ); }[/pre2] [pre2]#include "stdafx.h" #include <iostream> int _tmain(int argc, _TCHAR* argv[]) { static int x = 10; const int y = 20; auto z = [] { return ( x + y ); }; std::cout << "z() = " << z() << std::endl; return ( 0 ); }[/pre2] В обоих случаях компилятор MS VC++ выдает сообщение об ошибке. В первом случае текст сообщения об ошибке error C2326: int wmain::A::h(void): функция не может обратиться к "y" а во-втором error C3493: "y" нельзя передать неявно, поскольку не задан режим передачи по умолчанию Этот же код успешно компилируется и выполняется компилятором GCC 4.7.0.

Сыроежка: Как оказалось данный баг компилятора MS VC++ до сих пор не исправлен и имеет место быть даже в релизе компилятора MS VC++ 2013. Об этом сообщил на форуме MSDN Niels Dekker http://social.msdn.microsoft.com/Forums/vstudio/en-US/4abf18bd-4ae4-4c72-ba3e-3b13e7909d5f/error-c2057-or-c3493-trying-to-use-an-integral-constant-expression-inside-a-lambda?forum=vclanguage а также он оставил соответствующее сообщение в моем отчете о дефекте компилятора http://connect.microsoft.com/VisualStudio/feedback/details/725780/lambda-expressions-are-unable-to-use-constnat-variable-from-reaching-scope




полная версия страницы