Русская Википедия:Точка следования

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Точка следования (Шаблон:Lang-en) — в программировании любая точка программы, в которой гарантируется, что все побочные эффекты предыдущих вычислений уже проявились, а побочные эффекты последующих ещё отсутствуют.

Точки следования часто упоминают, когда речь идёт о языках C и C++. В этих языках можно записать выражение, порядок вычисления подвыражений которого не определён стандартами и влияет на результат. Добавление одной или нескольких точек следования позволяет гарантировать порядок вычисления в некоторых случаях.

Стоит заметить, что подход к упорядочению процесса вычисления выражений на основе точек следования изначально достаточно хорошо отвечал потребностям языка C, но не являлся адекватным для языка C++, в котором был существенно расширен набор операторов, возвращающих lvalue результаты. А с появлением необходимости языковой поддержки многопоточности в языках C и C++ от упорядочения на основе точек следования пришлось отказаться полностью. Современные спецификации языков C и C++ описывают упорядочение процесса вычисления выражений через отношения упорядочено до (sequenced before) и упорядочено после (sequenced after). Начиная со стандарта C++11, в языке C++ больше не существует понятия точки следования. В языке С понятие точки следования сохранилось по сей день, но, начиная со стандарта C11, не как фундаментальная концепция, а лишь как комбинация отношений упорядочено до и упорядочено после.

Стандарт C++11, а также последующие стандарты C++14 и C++17 внесли в операторы языка C++ большое количество дополнительных упорядочений на основе новой модели, что привело к тому, что многие выражения, поведение которых являлось неопределенным в C++98, получили вполне определённое поведение в современном C++. На сегодняшний день строгость упорядочения процесса вычисления выражений в языке C++ существенно превосходит таковую в языке C.

Примеры неоднозначности в языках C и C++

При наличии неоднозначностей стандарты языков C и C++:

  1. указывают несколько допустимых поведений из числа возможных (см. неуточняемое поведение);
  2. указывают единственно допустимое поведение из числа возможных либо
  3. явно указывают, что поведение не определено (см. неопределённое поведение).

Пример 1. Неуточняемое поведение.

g() + f()

Оператор «Шаблон:Cpp» не является точкой следования, поэтому неизвестно, какая из функций будет вызвана первой: Шаблон:Cpp или Шаблон:Cpp. Поведение зависит от реализации компилятора.

Пример 2. Единственно допустимое поведение.

f(), g()

Оператор «Шаблон:Cpp» является точкой следования, поэтому порядок вычисления гарантируется стандартом и известен заранее (слева направо):

  • сначала вычисляется левый операнд: вызывается функция Шаблон:Cpp;
  • затем — правый: вызывается функция Шаблон:Cpp.

Пример 3. Неопределённое поведение.

i = ++i + i++

С точки зрения языка C указанное выражение содержит множественные модификации переменной Шаблон:Cpp, не упорядоченные относительно друг друга. Поведение данного выражения не определено. (В то же время с точки зрения современного языка C++, который существенно более строго упорядочивает процесс вычисления оператора присваивания, поведение этого выражения полностью определено.)

Точки следования в языках C и C++

В оригинальных стандартах языков C и C++ были определены следующие точки следования:

  • точки следования для операторов «&&», «||» и «,». Эти операторы гарантированно вычисляются слева направо, если не перегружены. Пример. В выражении «Шаблон:Cpp» сначала вычисляется левый операнд («Шаблон:Cpp»); результат приводится к типу Шаблон:Cpp и сравнивается с Шаблон:Cpp; если равен Шаблон:Cpp, вычисляется правый операнд («Шаблон:Cpp»), иначе возвращается Шаблон:Cpp;
  • точка следования для тернарного оператора «?:». 1-й операнд вычисляется первым; затем располагается точка следования; 2-й операнд вычисляется только, если 1-й операнд равен Шаблон:Cpp; 3-й операнд вычисляется только, если 1-й операнд равен Шаблон:Cpp. Пример. В выражении «Шаблон:Cpp» сначала выполняется 1-й операнд («Шаблон:Cpp»; переменная Шаблон:Cpp увеличивается на Шаблон:Cpp); результат вычисления приводится к типу Шаблон:Cpp и сравнивается с Шаблон:Cpp; если равен Шаблон:Cpp, выполняется 2-й операнд («Шаблон:Cpp»), иначе — 3-й («0»);
  • точки следования в выражениях:
    • на месте символа «Шаблон:Cpp» в выражениях, являющихся отдельными инструкциями. Например, в выражении «Шаблон:Cpp» точка следования вставляется вместо «Шаблон:Cpp»;
    • в конце выражения, записанного после ключевого слова Шаблон:Cpp; а точнее, на момент, когда возвращаемое значение будет скопировано в контекст вызывающей функции. Эта точка следования явно описана только в стандарте C++;
    • в конце выражений, записанных в круглых скобках после ключевых слов Шаблон:Cpp, Шаблон:Cpp, Шаблон:Cpp (включая Шаблон:Cpp в конструкции do-while);
    • в концах каждого из трёх выражений для цикла Шаблон:Cpp;
  • перед вызовом функции. Порядок вычисления аргументов функции не определён. Точка следования гарантирует, что все аргументы будут вычислены до вызова функции. Пример. Рассмотрим выражение «Шаблон:Cpp». Сначала создаётся временная переменная со значением, равным значению переменной Шаблон:Cpp; затем для переменной Шаблон:Cpp(не для временной) вызывается оператор «постфиксный ++»; наконец, вызывается функция Шаблон:Cpp с временной переменной в качестве аргумента. Сказанное справедливо для переменных Шаблон:Cpp, Шаблон:Cpp и функций Шаблон:Cpp, Шаблон:Cpp соответственно. При этом из-за отсутствия точки следования у оператора «+» порядок вызова функций Шаблон:Cpp, Шаблон:Cpp и Шаблон:Cpp не определён. Следовательно не определён и порядок вызова операторов «постфиксный ++» для переменных Шаблон:Cpp, Шаблон:Cpp и Шаблон:Cpp. То есть при выполнении функции Шаблон:Cpp неизвестно, были ли вызваны операторы «постфиксный ++» для переменных Шаблон:Cpp и Шаблон:Cpp. Пример. Рассмотрим выражение «Шаблон:Cpp». Запятая между аргументами функции не является оператором «запятая» и не гарантирует порядок вычисления значений аргументов. Порядок вычисления значений аргументов функции не стандартизован и зависит от реализации компилятора;
  • в объявлении с инициализацией на момент завершения вычисления инициализирующего значения. Пример. Рассмотрим выражение «Шаблон:Cpp». Точка следования вставляется после вычисления выражения «Шаблон:Cpp»;
  • перед вызовом перегруженного оператора в языке C++. Точка следования гарантирует вычисление значений аргументов оператора (как и обычной функции) до его вызова.

См. также

Примечания

Шаблон:Примечания

Ссылки

Шаблон:Rq