Самый неприятный разбор
этой статьи Начальный раздел может быть слишком коротким, чтобы адекватно суммировать ключевые моменты . ( май 2024 г. ) |
Самый неприятный синтаксический анализ — это нелогичная форма разрешения синтаксической неоднозначности в языке программирования C++ . В определенных ситуациях грамматика C++ не может отличить создание объекта параметра от указания типа функции . В таких ситуациях компилятор должен интерпретировать строку как спецификацию типа функции.
возникновение
[ редактировать ]Термин «самый неприятный анализ» впервые был использован Скоттом Мейерсом в его книге «Эффективный STL» 2001 года . [1] Хотя это явление необычно для C , оно было довольно распространено в C++ до появления единообразной инициализации в C++11 . [2]
Примеры
[ редактировать ]Приведения в стиле C
[ редактировать ]Простой пример появляется, когда функциональное приведение предназначено для преобразования выражения для инициализации переменной:
void f(double my_dbl) {
int i(int(my_dbl));
}
Строка 2 выше неоднозначна. Одна из возможных интерпретаций — объявить переменную i
с начальным значением, полученным путем преобразования my_dbl
к int
. Однако C допускает использование лишних круглых скобок вокруг параметров функции объявлений ; в этом случае декларация i
вместо этого является объявлением функции, эквивалентным следующему:
// A function named i takes an integer and returns an integer.
int i(int my_dbl);
Безымянный временный
[ редактировать ]Более подробный пример:
struct Timer {};
struct TimeKeeper {
explicit TimeKeeper(Timer t);
int get_time();
};
int main() {
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}
Линия
TimeKeeper time_keeper(Timer());
неоднозначно, поскольку его можно интерпретировать либо как
- определение переменной для переменной
time_keeper
классаTimeKeeper
, инициализированный анонимным экземпляром классаTimer
или - для объявление функции функции
time_keeper
который возвращает объект типаTimeKeeper
и имеет один (безымянный) параметр, тип которого является функцией (указатель на) [Примечание 1] не принимать никаких данных и возвращатьTimer
объекты.
Стандарт C++ требует второй интерпретации, которая несовместима с последующей строкой 10 выше. Например, Clang++ предупреждает, что самый неприятный анализ был применен в строке 9 и ошибки в последующей строке 10: [3]
$ clang++ time_keeper.cc timekeeper.cc:9:25: warning: parentheses were disambiguated as a function declaration [-Wvexing-parse] TimeKeeper time_keeper(Timer()); ^~~~~~~~~ timekeeper.cc:9:26: note: add a pair of parentheses to declare a variable TimeKeeper time_keeper(Timer()); ^ ( ) timekeeper.cc:10:21: error: member reference base type 'TimeKeeper (Timer (*)())' is not a structure or union return time_keeper.get_time(); ~~~~~~~~~~~^~~~~~~~~
Решения
[ редактировать ]Требуемая интерпретация этих двусмысленных заявлений редко соответствует действительности. [4] [5] Типы функций в C++ обычно скрыты за определениями типов и обычно имеют явную ссылку или квалификатор указателя . Чтобы обеспечить альтернативную интерпретацию, типичным методом является другой синтаксис создания или преобразования объектов.
В примере преобразования типов для приведения доступны два альтернативных синтаксиса: «приведение в стиле C».
// declares a variable of type int
int i((int)my_dbl);
или именованный состав:
int i(static_cast<int>(my_dbl));
В примере объявления переменной предпочтительным методом (начиная с C++11) является унифицированная инициализация (фигурные скобки). [6] Это также позволяет ограниченно полностью опускать имя типа:
//Any of the following work:
TimeKeeper time_keeper(Timer{});
TimeKeeper time_keeper{Timer()};
TimeKeeper time_keeper{Timer{}};
TimeKeeper time_keeper( {});
TimeKeeper time_keeper{ {}};
До C++11 распространенными методами принудительной интерпретации было использование дополнительных круглых скобок или инициализация копирования: [5]
TimeKeeper time_keeper( /*Avoid MVP*/ (Timer()) );
TimeKeeper time_keeper = TimeKeeper(Timer());
В последнем синтаксисе инициализация копирования , скорее всего, будет оптимизирована компилятором. [7] Начиная с C++17 эта оптимизация гарантирована. [8]
Примечания
[ редактировать ]- ^ Согласно правилам распада типов C++, объект функции, объявленный как параметр, эквивалентен указателю на функцию этого типа. См. Функциональный объект#В C и C++ .
Ссылки
[ редактировать ]- ^ Мейерс, Скотт (2001). Эффективный STL: 50 конкретных способов улучшить использование стандартной библиотеки шаблонов . Аддисон-Уэсли. ISBN 0-201-74962-9 .
- ^ Гроб, Джерри (29 декабря 2012 г.). «С++ — какова цель самого неприятного синтаксического анализа?» . Переполнение стека . Архивировано из оригинала 17 января 2021 года . Проверено 17 января 2021 г.
- ^ Латтнер, Крис (5 апреля 2010 г.). «Удивительные возможности восстановления ошибок Clang» . Блог проекта LLVM . Самый неприятный разбор. Архивировано из оригинала 26 сентября 2020 года . Проверено 17 января 2021 г.
- ^ ДокторПицца; Прототипированный; ВБ; эвзека; Симпсон, Гомер Дж. (октябрь 2002 г.). «Самый неприятный синтаксический анализ C++ » . Открытый форум АрсТехника . Архивировано из оригинала 20 мая 2015 года . Проверено 17 января 2021 г.
- ^ Jump up to: а б Боккара, Джонатан (30 января 2018 г.). «Самый неприятный анализ: как его обнаружить и быстро исправить» . Свободное владение С++ . Архивировано из оригинала 25 ноября 2021 г. Проверено 17 января 2021 г.
- ^ Страуструп, Бьярне (19 августа 2016 г.). «Часто задаваемые вопросы по C++11» . www.stroustrup.com . Единый синтаксис и семантика инициализации. Архивировано из оригинала 20 августа 2021 г. Проверено 17 января 2021 г.
- ^ «Мифы и городские легенды о C++» . Часто задаваемые вопросы по C++ . Что такое копирование? Что такое РВО?. Архивировано из оригинала 17 января 2021 года . Проверено 17 января 2021 г.
- ^ Девлигер, Йонас (21 ноября 2016 г.). «Гарантированное исключение копирования» . Йонас Девлигер . Архивировано из оригинала 25 ноября 2021 г. Проверено 17 января 2021 г. Однако обратите внимание на предостережения, описанные в Бренд, C++ (11 декабря 2018 г.). «Гарантированное удаление копий не приводит к удалению копий» . Блог группы Microsoft C++ . Архивировано из оригинала 25 ноября 2021 г. Проверено 17 января 2021 г.
Внешние ссылки
[ редактировать ]- Обсуждение окончательного проекта стандарта C++03 (см. §8.2 Разрешение неоднозначности [dcl.ambig.res] ): https://web.archive.org/web/20141113085328/https://cs.nyu.edu/courses /fall11/CSCI-GA.2110-003/documents/c++2003std.pdf
- CppReference при прямой инициализации (вид, уязвимый для самого неприятного анализа): https://en.cppreference.com/w/cpp/language/direct_initialization. Архивировано 13 декабря 2021 г. на Wayback Machine.