Чередование функций в pre C ++ 17 - PullRequest
3 голосов
/ 24 мая 2019

Посмотрите на этот простой вызов функции:

f(a(), b());

Согласно стандарту, порядок звонков a() и b() не указан. C ++ 17 имеет дополнительное правило, которое не допускает чередования a() и b(). До C ++ 17, насколько я знаю, такого правила не было.

Теперь посмотрите на этот простой код:

int v = 0;

int fn() {
    int t = v+1;
    v = t;
    return 0;
}

void foo(int, int) { }

int main() {
    foo(fn(), fn());
}

С правилами C ++ 17, v обязательно будет иметь значение 2 после вызова foo. Но, это заставляет меня задаться вопросом, с pre-C ++ 17, то же самое гарантировано? Или, может быть, v заканчивается 1? Имеет ли это значение, если вместо int t = v+1; v = t; у нас просто есть v++?

Ответы [ 4 ]

6 голосов
/ 24 мая 2019

Вызовам функций также не разрешалось чередоваться в предыдущих версиях.

Цитирование из окончательного черновика C ++ 11 (n3337)

1.9 Выполнение программы [intro.execution]
...

15 .... При вызове функции (независимо от того, является ли функция встроенной), каждое вычисление значения и побочный эффект, связанные с любым выражением аргумента или с выражением постфикса, обозначающим вызываемую функцию, упорядочиваются перед выполнением каждого выражения или оператора втело вызываемой функции.[Примечание: вычисления значений и побочные эффекты, связанные с различными выражениями аргументов, не являются последовательными.- примечание конца] Каждая оценка в вызывающей функции (включая другие вызовы функций), которая иначе специально не упорядочена до или после выполнения тела вызываемой функции, определяется неопределенным образом по отношению к выполнению вызываемой функции 9.


9) Другими словами, исполнения функций не чередуются друг с другом .

Подобную формулировку можно найтив окончательном варианте версии C ++ 14.

2 голосов
/ 24 мая 2019

C ++ 17 имеет дополнительное правило, которое не допускает чередование a () и b ().До C ++ 17, насколько я знаю, такого правила не было.

Здесь применялись правила, хотя формулировка и некоторые детали стали более точными.

C ++ 03 [intro.execution] / 8:

Как только начинается выполнение функции, никакие выражения из вызывающей функции не вычисляются до тех пор, пока не завершится выполнение вызываемой функции.[сноска 8]

[сноска 8]: Другими словами, выполнение функций не чередуется друг с другом.

Хотя вы можете утверждать, что это ничего не говорит о другихфункции, вызываемые из вызывающей функции в тексте, и сноски официально не являются нормативными.

C ++ 11 изменил формулировку, главным образом потому, что ввел семантику многопоточности.C ++ 11 и C ++ 14 [intro.execution] / 15, выделено:

При вызове функции (независимо от того, является ли функция встроенной), каждое вычисление значения и побочный эффект связаныс любым выражением аргумента или с выражением постфикса, обозначающим вызываемую функцию, секвенируется перед выполнением каждого выражения или оператора в теле вызываемой функции.[ Примечание: Вычисления значений и побочные эффекты, связанные с различными выражениями аргументов, не являются последовательными.- конечная нота ] Каждая оценка в вызывающей функции (включая вызовы других функций), которая иначе специально не упорядочена до или после выполнения тела вызываемой функции, определяется неопределенным образом по отношению квыполнение вызванной функции. [сноска 9]

[сноска 9] Другими словами, выполнение функций не чередуется друг с другом.

Эта формулировка, я думаю, не оставляет сомнений, по крайней мере, в большинстве случаев.

C ++ 17 [intro.execution] / 18:

При вызове функции (независимо от того, является ли функция встроенной или нет), каждое вычисление значения и побочный эффект, связанный с любым выражением аргумента или с выражением постфикса, обозначающим вызываемую функцию, упорядочивается перед выполнением каждого выражения или оператора в теле вызываемой функции.Для каждого вызова функции F , для каждой оценки A , которая происходит в F , и для каждой оценки B , которая не происходит в F , но оценивается в том же потоке и как часть того же обработчика сигнала (если есть), либо A секвенируется до B или B последовательность перед A .[сноска 10] [ Примечание: Если A и B иначе не будут упорядочены, то они упорядочены неопределенно.- конец примечания ]

[сноска 10] Другими словами, выполнение функций не чередуется друг с другом.

Это гораздо более общее утверждение овсе вычисления в отдельных функциях, а не просто аргументы в вызове функции.Но, насколько мне известно, эта более точная формулировка только проясняет некоторые, возможно, неоднозначные случаи, но на самом деле не сильно меняет семантическое поведение.

2 голосов
/ 24 мая 2019

v должно быть 2 во всех версиях C ++ (и C).Функция fn() должна выполняться дважды, и, очевидно, что при каждом ее выполнении она будет увеличиваться v.Здесь нет многопоточности, нет гонки данных и нет возможности выполнить fn() только частично, а затем прервать его, пока продолжается другой вызов fn().

0 голосов
/ 24 мая 2019

Я подозреваю, что мы путаем две концепции. Порядок вызовов был исправлен только в выражениях в c ++ 17. Чередование операторов в функции между двумя вызовами функций всегда было запрещено, хотя, когда оптимизатор получает ваш код, гарантируется только эффект.

Если вы напишите a() ; b(), тогда a () будет полностью выполнен, прежде чем b будет полностью выполнен. То же самое, если вы напишите a(), b() или a() && b()

Если вы напишите a() + b(), то до c ++ 17 нет гарантии того, что a () будет выполнено до b (), но есть гарантия, что любое из выполненных первым будет полностью завершено до того, как другое будет выполнено. казнены.

Если вы напишите c(a(), b()), это [насколько я понимаю], опять же, нет никакой гарантии, что a () будет выполнена перед b (), но есть / есть / гарантия того, что результаты первой выполненной функции (в зависимости от того, что то есть) завершится до запуска второго, и, конечно, есть гарантия, что c () не будет выполнена, пока не завершатся оба a () и b ().

Оптимизатор должен соблюдать намерения этой гарантии, хотя он может принимать операторы a () и b () и даже c () и смешивать их, эффект запуска кода должно быть таким же, как если бы они были запущены в последовательности. Может ли код -O0 выполнить a (), а затем b (), а код -O3 выполнить b (), а затем a ()? Возможно, хотя это и затруднит отладку, так что я надеюсь, что нет.

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

Насколько я понимаю, оптимизатор не может допустить, чтобы специфические эффекты a (), b () и c () появлялись не по порядку между каждой функцией, хотя до c ++ 17 порядок a () и b () не определены четко - все эффекты b () могут возникать раньше всех эффектов a ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...