Я читал о порядке нарушений оценок, и они приводят пример, который меня озадачивает.
1) Если побочный эффект скалярного объекта не секвенирован относительно другого побочного эффекта на том же скалярном объекте, поведение undefined.
// snip f(i = -1, i = -1); // undefined behavior
В этом контексте i
является скалярным объектом, который, по-видимому, означает
Арифметические типы (3.9.1), типы перечислений, типы указателей, указатель на типы членов (3.9.2), std:: nullptr_t и cv-квалификационные версии этих типов (3.9.3), совокупно называются скалярными типами.
Я не вижу, как в этом случае утверждение неоднозначно. Мне кажется, что независимо от того, сначала оценивается первый или второй аргумент, i
заканчивается как -1
, и оба аргумента также -1
.
Кто-нибудь может прояснить?
UPDATE
Я очень ценю всю дискуссию. Пока мне нравится @harmics, поскольку он раскрывает подводные камни и тонкости определения этого утверждения, несмотря на то, как прямо он смотрит на первый взгляд. @acheong87 указывает на некоторые проблемы, возникающие при использовании ссылок, но я считаю, что это ортогонально к аспекту побочных эффектов, не связанных с побочными эффектами.
СУЩНОСТЬ
Поскольку этот вопрос получил тонну внимания, я подведу основные моменты/ответы. Во-первых, позвольте мне дать небольшое отступление, чтобы указать, что "почему" может иметь тесно связанные, но тонко разные значения, а именно "по какой причине" , "по какой причине" и "с какой целью". Я буду группировать ответы, по каким из этих значений "почему" они обращаются.
по какой причине
Главный ответ здесь - "Paul Draper, с Мартин J, способствующий аналогичному, но не столь обширному ответ. Ответ Пол Дрейпера сводится к
Это поведение undefined, потому что не определено, что такое поведение.
Ответ в целом очень хорош с точки зрения объяснения того, что говорит стандарт С++. В нем также рассматриваются некоторые связанные случаи UB, такие как f(++i, ++i);
и f(i=1, i=-1);
. В первом из связанных случаев не ясно, должен ли первый аргумент быть i+1
, а второй i+2
или наоборот; во втором, неясно, должно ли i
быть 1 или -1 после вызова функции. Оба эти случая - UB, потому что они подпадают под следующее правило:
Если побочный эффект скалярного объекта не зависит от другого побочного эффекта на одном и том же скалярном объекте, поведение undefined.
Следовательно, f(i=-1, i=-1)
также является UB, поскольку он подпадает под одно и то же правило, несмотря на то, что намерение программиста (IMHO) очевидно и недвусмысленно.
Пол Дрейпер также делает это в своем заключении, что
Может ли быть определено поведение? Да. Было ли это определено? Нет.
что подводит нас к вопросу "по какой причине/цели был f(i=-1, i=-1)
оставлен как поведение undefined?"
по какой причине/цели
Несмотря на некоторые недочеты (возможно, неосторожные) в стандарте С++, многие упущения хорошо аргументированы и служат определенной цели. Хотя я знаю, что цель часто либо "облегчает работу с компилятором-писателем", либо "более быстрый код", Мне было интересно узнать, есть ли повод по уважительной причине f(i=-1, i=-1)
как UB.
harmic и supercat предоставляют основные ответы, которые дают основание для UB. Harmic указывает, что оптимизирующий компилятор, который может разбить якобы атомарные операции назначения на несколько машинных инструкций, и что он может дополнительно чередовать эти инструкции для оптимальной скорости. Это может привести к некоторым неожиданным результатам: i
заканчивается как -2 в его сценарии! Таким образом, harmic демонстрирует, как присвоение одного и того же значения переменной более одного раза может иметь негативные последствия, если операции не подвержены влиянию.
supercat предоставляет связанное изложение подводных камней попыток получить f(i=-1, i=-1)
, чтобы сделать то, что, похоже, должно делать. Он указывает, что на некоторых архитектурах существуют жесткие ограничения на одновременную запись нескольких одновременных записей на один и тот же адрес памяти. Компилятору нелегко было бы это поймать, если бы мы имели дело с чем-то менее тривиальным, чем f(i=-1, i=-1)
.
davidf также предоставляет пример команд чередования, очень похожих на harmic's.
Хотя каждый из примеров harmic, supercat и davidf несколько надуман, вместе взятые они все еще служат для того, чтобы обеспечить ощутимую причину, по которой f(i=-1, i=-1)
должно быть undefined.
Я принял негативный ответ, потому что он наилучшим образом справился со всеми соображениями о том, почему, хотя Пол Дрейпер ответил на вопрос "по какой причине" .
другие ответы
JohnB указывает, что если мы рассмотрим перегруженные операторы присваивания (вместо простых скаляров), тогда мы также столкнемся с проблемами.