функция против объявления переменных в C ++ - PullRequest
12 голосов
/ 12 сентября 2010

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

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)? Или GCC обрабатывает это правильно? Это неопределено в стандарте C ++?

1 Ответ

14 голосов
/ 12 сентября 2010

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

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

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(...).

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

...