Форум » C/C++ » MS VC++ 2010: Самый необычный баг компилятора » Ответить

MS VC++ 2010: Самый необычный баг компилятора

Сыроежка: Пожалуй описанный далее баг компилятора MS VC++ 2010 является самым необычным и неожиданным. Он никак не связан с какой-нибудь запутанной семантикой языка С++, и проявляется там, где меньше всего его ждешь. Обнаружил я его совершенно случайно, отвечая на один вопрос на форуме по С++. Имеется класс [pre2]class Array { public: enum { N = 5 }; Array( int value = 0 ) { for ( size_t i = 0; i < N; i++ ) { for ( size_t j = 0; j < N; j++ ) { a[ i ][ j ] = value; } } } private: int a[N][N]; };[/pre2] Спрашивается: как перегрузить для него оператор индексирования, чтобы можно было обращаться к отдельным элементам массива, являющегося членом класса. То есть если в программе объявлен объект данного типа, то хотелось бы иметь привычный синтаксис обращения к элементам этого объекта, что-то вроде: [pre2]Array a; for ( size_t i = 0; i < Array::N; i++ ) { std::for_each( a[ i ], a[ i ] + Array::N, [] ( int x ) { std::cout << x << ' '; } ); std::cout << std::endl; }[/pre2] Напрашивается решение перегрузить оператор [] для этого класса, который будет возвращать ссылку на заданную строку массива. И я предложил такое решение [pre2]#include "stdafx.h" #include <algorithm> #include <numeric> #include <iostream> class Array { public: enum { N = 5 }; Array( int value = 0 ) { for ( size_t i = 0; i < N; i++ ) { for ( size_t j = 0; j < N; j++ ) { a[ i ][ j ] = value; } } } int ( & operator []( size_t i ) )[N] { return ( a[ i ] ); } private: int a[N][N]; }; int _tmain(int argc, _TCHAR* argv[]) { Array a; std::iota( a[1], a[1] + Array::N, 1 ); for ( size_t i = 0; i < Array::N; i++ ) { std::for_each( a[ i ], a[ i ] + Array::N, [] ( int x ) { std::cout << x << ' '; } ); std::cout << std::endl; } return 0; }[/pre2] В этом примере создается объект класса Array с именем a, у которого все эелменты массива заполняются по умолчанию нулями. Затем с помощью алгоритма std::iota заполняется вторая сттрока (с индексом 1) массива возрастающими значениями, начиная с 1, чтобы проверить работу предложенного перегруженного оператора индексирования. Ну, а после, сам массив объекта выводится на консоль. К моему удивлению компилятор выдал следующие сообщения об ошибках: [quote]error C2061: синтаксическая ошибка: идентификатор "size_t" error C2805: бинарный "operator [" имеет слишком мало параметров error C2333: Array::operator []: ошибка в объявлении функции; пропуск основного текста функции error C2676: бинарный "[": "Array" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору error C2676: бинарный "[": "Array" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору error C2676: бинарный "[": "Array" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору error C2676: бинарный "[": "Array" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору[/quote] Здесь важны первые два сообщения об ошибке, потому что остальные сообщения являются лишь следствием этих двух сообщений: [quote]error C2061: синтаксическая ошибка: идентификатор "size_t" error C2805: бинарный "operator [" имеет слишком мало параметров[/quote] Эти сообщения об ошибках довольно странные. С чего это, вдруг, компилятор жалуется на идентификатор size_t?! Я сначала подумал, что может быть надо указать тип параметра с префиксом пространства имен, то есть в виде: std::size_t. Но ничего не изменилось. По-прежнему выдавались ошибки компиляции. Тогда я подключил заголовок, где явно объявлен тип size_t, а именно <cstdlib>. Но и это не помогло. Естественно пришла идея проверить код с помощью другого компилятора. Как и следовало ожидать, компилятор GCC 4.7 скомпилировал код без ошибок. Но затем мне пришла мысль: если компилятор ругается на size_t, то что если заменить size_t на int? Я так и сделал, то есть определение перегруженного оператора записал следующим образом: [pre2]int ( & operator []( int i ) )[N] { return ( a[ i ] ); }[/pre2] По сравнению с исходным оператором здесь изменен только тип единственного параметра. И неожиданно, как по мановению волшебной палочки, код скомпилировался и выполнился! То есть компилятор MS VC++ 2010 категорически не принимал в качестве типа параметра только тип siize_t, что, естественно, очень странно и ничем не обосновано. Исходный код с size_t совершенно корректный и должен был компилироваться. Это очень необычный и неожиданный баг компилятора MS VC++ 2010. Естественно я отослал в Майкрософт сообщение об этом странном баге. Понять самостоятельно, почему компилятор не воспринимает тип size_t для параметра именно этой оператор-функции, невозможно. Никаких зацепок нет.

Ответов - 1

Сыроежка: Пришел ответ от Майкрософт, в котором они подтвердили о наличии бага: Posted by Microsoft on 15.05.2012 at 15:37 Hello, Thank you for reporting. We will consider fixing this in a future release. For some reason typedef being used in the operator signature is causing the error. In the meantime, can you please accept this workaround? int a[N][N]; typedef int (&array_ref)[N]; array_ref operator []( std::size_t i ) { return ( a[ i ] ); } Thanks, Ulzii Luvsanbat Visual C++ Team Я проверил предложенный ими обходной вариант. Действительно, если задать typedef вида [pre2]typedef int ( &array_ref )[N];[/pre2] и переписать оператор-функцию с использованием этого определения, [pre2]array_ref operator []( std::size_t i ) { return ( a[ i ] ); }[/pre2] то код будет успешно компилироваться. Очень необычный баг!



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