Подтвердить что ты не робот

Почему a = я + я ++ undefined, а не неопределенное поведение

Я прочитал несколько очень хороших ответов о поведении и точках последовательности undefined (например, Undefined поведение и точки последовательности), и я понимаю, что

   int i = 1;
   a = i + i++; //this is undefined behaviour

- это код undefined, в соответствии со стандартом С++. Но каково более глубокое рассуждение о том, что это поведение undefined? Разве этого было бы недостаточно, чтобы сделать это неуказанным поведением? Нормальный аргумент состоит в том, что, имея несколько точек последовательности, компиляторы С++ могут лучше оптимизироваться для разных архитектур, но не оставляя его неуказанным, позволяют эти оптимизации также? В

   a = foo(bar(1), bar(2)); //this is unspecified behaviour

компилятор также может оптимизировать, и это не поведение undefined. В первом примере кажется ясным, что a является либо 2, либо 3, поэтому семантика кажется мне понятной. Я надеюсь, что есть рассуждение, почему некоторые вещи неуточнены, а другие - undefined.

4b9b3361

Ответ 1

С точки зрения С++, я считаю, что ответ невероятно прост: он был выполнен как undefined, потому что C ранее делал его undefined, и по существу не было никакого потенциального выигрыша от его изменения.

Это указывает на то, что, на мой взгляд, было бы более насущным вопросом: почему C сделал это поведение undefined?

Я не думаю, что это имеет довольно простой ответ. Одна из возможностей - простое предостережение - знание того, что к моменту написания стандарта C C уже был реализован, развернут и использован на множестве машин. Справедливое количество машин в то время казалось большим количеством кода, который я все еще вижу: что-то изначально было разработано только как персональный эксперимент, который работал достаточно хорошо, что он оказался обозначенным как "производство", даже не предприняв попыток зафиксировать что-либо самые вопиющие проблемы. Таким образом, даже если никто не знал об аппаратном обеспечении, это сломалось бы, никто не мог быть действительно уверен, что такого оборудования тоже не существует, поэтому было бы безопаснее просто называть его UB и делать с ним.

Другая возможность заключается в том, что она немного отличалась от простой осторожности. Несмотря на то, что мы можем чувствовать себя в безопасности с современным оборудованием, возможно, в то время, когда люди действительно знали, у него были бы серьезные проблемы с этим, и (особенно если поставщики, связанные с этим оборудованием, были представлены в комитете), позволяя C работать это аппаратное обеспечение считалось важным.

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

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

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

Ответ 2

Не все эти оптимизации. Например, Itanium может выполнять как добавление, так и прирост параллельно, и он может кашлять, скажем, аппаратное исключение, пытаясь сделать что-то вроде этого.

Но это полностью микро-оптимизация, писать компилятор, чтобы воспользоваться этим было чрезвычайно сложно, и это очень редкая архитектура, которая может это сделать (в то время не было ни одного, IIRC, это было в основном гипотетическим). Таким образом, реальность такова, что с 2012 года нет причин для того, чтобы это не было четко определенным поведением, и действительно, С++ 11 четко определил эти ситуации.

Ответ 3

Там существует огромная разница между поведением undefined и неуказанным поведением. Неопределенное поведение является корректным (то есть законным), но стандарт оставляет компилятором ограниченную доступность для реализации. undefined поведение - это жестокость, которая кажется синтаксически правильной. Основная причина того, что поведение поведении "undefined", а не flat-out незаконно (что-то компилятор должен отклонить), заключается в том, что иногда поведение undefined может быть очень сложно диагностировать.

Ответ 4

a = foo(bar(1), bar(2)); // this is unspecified behaviour

Два вызова функций могут выполняться в любом порядке, но они остаются двумя различными вызовами функций. Не допускается наложение на машинные команды, даже если вызовы встроены. В действительности, они частично перекрываются, но оптимизатор ограничен созданием кода, который ведет себя строго, как если бы вызовы функций были отдельными.

a = i + i++; // this is undefined behaviour

С скаляром я нет требования для разделения: инструкции cpu, которые извлекают из i, которые добавляют, и что postincrement, смешиваются свободно, и оптимизатору разрешено делать вид, что он не знает, что я слева и я справа - одно и то же. Там не сказано, какую сломанную сборку он может произвести, когда это предварительное условие нарушено. Таким образом, undefined.

Ответ 5

MIPS 1 была разумной реализацией с слотами задержки загрузки. Выполнение нагрузки не будет мгновенным. Результат был видимым только после того, как была запущена следующая инструкция. Для компиляторов это было неважно. Просто поставьте несвязанные инструкции в следующий слот.

Конечно, компилятор должен был знать, что такое "несвязанный". С правилом C против одновременных модификаций одной varibale у компилятора был гораздо больше выбора в поиске инструкции, которая должна была быть несвязанной. Если две операции появились в одном утверждении, они должны работать с разными переменными и поэтому не иметь отношения.