Подтвердить что ты не робот

Функция vs объявление переменной в С++

Этот код работает:

std::ifstream f(mapFilename.c_str());
std::string s = std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
ParseGameState(s);

Таким образом, mapFilename является std::string и void ParseGameState(const std::string&);.

И это не так:

std::ifstream f(mapFilename.c_str());
std::string s(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
ParseGameState(s);

Это ошибка:

game.cpp: In member function ‘int Game::LoadMapFromFile(const std::string&)’:
game.cpp:423: error: no matching function for call to ‘ParseGameState(std::string (&)(std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> > (*)()))’
game.cpp:363: note: candidates are: ParseGameState(const std::string&)

Так кажется, что он распознает s как объявление функции, а не объявление переменной в этом случае.

Почему? Это ошибка в GCC 4.2.1 (Apple build)? Или GCC правильно справляется? Это undefined в стандарте С++?

4b9b3361

Ответ 1

Это С++ "самый неприятный синтаксический анализ". Быстрый Google для этого должен показать множество хитов с большим количеством деталей. Основной ответ заключается в том, что да, компилятор рассматривает это как объявление функции, а С++ требует, чтобы он это делал. Нет ничего плохого в вашем компиляторе (по крайней мере в этом отношении).

Если вам будет удобно, у вас будет много хорошей компании, столкнувшись с этим. На самом деле, достаточно распространено то, что С++ 0x добавлен новый синтаксис синтаксиса brace-initializer, во многом потому, что он избегает этой двусмысленности. Используя его, вы можете написать что-то вроде:

std::string s{std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};

Это даст понять, что содержимое фигурных скобок предназначено для значений инициализации s, а не типов параметров для функции с именем s. Я не знаю, есть ли у Apple порт, но gcc принимает новый синтаксис версии 4.5 (или так).

Изменить: Перечитывая N3092, Йоханнес (как обычно) вполне корректен. Применяемый язык (§8.5.4/3/5): "Если T имеет конструктор списка инициализаторов, список аргументов состоит из списка инициализаторов как один аргумент, в противном случае список аргументов состоит из элементов инициализатора список".

Итак, поскольку std::string имеет конструктор-инициализатор-список, это попытается "заполнить" два istreambuf_iterator в списке инициализаторов и передать это в std::string ctor, который принимает список инициализаторов, но это будет несоответствие типа, поэтому код не может скомпилироваться. Для другого типа типа, в котором (в отличие от std::string не было списка инициализаторов ctor), преобразование выше работало бы (спасибо "в противном случае..." в приведенной выше цитате). В случае std::string вам придется использовать одну из существующих альтернатив, например std::string s = std:string(...).

Я извиняюсь за неправильное предложенное исправление - в этом случае, все хуже, потому что он смущает проблему, которая, вероятно, будет чрезмерно запутанной сама по себе, и если что-то потребует тщательного разъяснения, особенно в течение следующих нескольких лет.