Пожалуй описанный далее баг компилятора MS VC++ 2010 является самым необычным и неожиданным. Он никак не связан с какой-нибудь запутанной семантикой языка С++, и проявляется там, где меньше всего его ждешь.
Обнаружил я его совершенно случайно, отвечая на один вопрос на форуме по С++. Имеется класс
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];
};
Спрашивается: как перегрузить для него оператор индексирования, чтобы можно было обращаться к отдельным элементам массива, являющегося членом класса. То есть если в программе объявлен объект данного типа, то хотелось бы иметь привычный синтаксис обращения к элементам этого объекта, что-то вроде:
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;
}
Напрашивается решение перегрузить оператор [] для этого класса, который будет возвращать ссылку на заданную строку массива. И я предложил такое решение
#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;
}
В этом примере создается объект класса
Array с именем
a, у которого все эелменты массива заполняются по умолчанию нулями. Затем с помощью алгоритма
std::iota заполняется вторая сттрока (с индексом 1) массива возрастающими значениями, начиная с 1, чтобы проверить работу предложенного перегруженного оператора индексирования. Ну, а после, сам массив объекта выводится на консоль.
К моему удивлению компилятор выдал следующие сообщения об ошибках:
цитата: |
error C2061: синтаксическая ошибка: идентификатор "size_t" error C2805: бинарный "operator [" имеет слишком мало параметров error C2333: Array::operator []: ошибка в объявлении функции; пропуск основного текста функции error C2676: бинарный "[": "Array" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору error C2676: бинарный "[": "Array" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору error C2676: бинарный "[": "Array" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору error C2676: бинарный "[": "Array" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору |
|
Здесь важны первые два сообщения об ошибке, потому что остальные сообщения являются лишь следствием этих двух сообщений:
цитата: |
error C2061: синтаксическая ошибка: идентификатор "size_t" error C2805: бинарный "operator [" имеет слишком мало параметров |
|
Эти сообщения об ошибках довольно странные. С чего это, вдруг, компилятор жалуется на идентификатор
size_t?!
Я сначала подумал, что может быть надо указать тип параметра с префиксом пространства имен, то есть в виде:
std::size_t. Но ничего не изменилось. По-прежнему выдавались ошибки компиляции. Тогда я подключил заголовок, где явно объявлен тип
size_t, а именно
<cstdlib>. Но и это не помогло.
Естественно пришла идея проверить код с помощью другого компилятора. Как и следовало ожидать, компилятор GCC 4.7 скомпилировал код без ошибок.
Но затем мне пришла мысль: если компилятор ругается на
size_t, то что если заменить
size_t на
int? Я так и сделал, то есть определение перегруженного оператора записал следующим образом:
int ( & operator []( int i ) )[N]
{
return ( a[ i ] );
}
По сравнению с исходным оператором здесь изменен только тип единственного параметра. И неожиданно, как по мановению волшебной палочки, код скомпилировался и выполнился!
То есть компилятор MS VC++ 2010 категорически не принимал в качестве типа параметра только тип
siize_t, что, естественно, очень странно и ничем не обосновано. Исходный код с
size_t совершенно корректный и должен был компилироваться.
Это очень необычный и неожиданный баг компилятора MS VC++ 2010.
Естественно я отослал в Майкрософт сообщение об этом странном баге.
Понять самостоятельно, почему компилятор не воспринимает тип
size_t для параметра именно этой оператор-функции, невозможно. Никаких зацепок нет.