Когда можно делать/использовать что-то с неопределенным поведением?

В C++ появляются вещи, находящиеся где-то между четко определенными и неопределенными. В частности, они называются реализация определена и не определена. Сейчас меня интересуют неуказанные вещи.

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

Определения предоставлены Мэттом Макнаббом:

  • Не определено - может случиться что угодно

  • Определяется реализацией - возможно конечное количество результатов, и в документации компилятора должно быть указано, что происходит.

  • Не указано — возможно конечное число результатов — обычно Стандарт описывает набор возможных результатов.

  • Четко определено - ничего из вышеперечисленного

  • Хорошо сформированная программа - программа, которая компилируется без ошибок (может демонстрировать неопределенное поведение)

Дополнительный вопрос:

Считаются ли расслабленные атомы неопределенными или четко определенными?

Помечено как дубликат вопроса, в котором та же идея рассматривается с другой точки зрения. Вопрос, помеченный как то же самое, говорит об определении неопределенного поведения, тогда как здесь вопрос о том, как и когда его использовать.


person Michael Gazonda    schedule 17.07.2014    source источник
comment
Вы имеете в виду реализацию? Если нет, то я не могу понять вопрос.   -  person Jon    schedule 18.07.2014
comment
Вроде как есть только четко определенные и прочие.   -  person user2864740    schedule 18.07.2014
comment
Вы имеете в виду как int arr[5]{}; someCondition ? arr[0] : arr[10];? Извините, это единственное, что я могу придумать навскидку, и оно не очень полезно ни для чего, кроме категоризации.   -  person chris    schedule 18.07.2014
comment
вы должны привести конкретный пример, иначе этот вопрос слишком широк.   -  person Ahmed Masud    schedule 18.07.2014
comment
Не все, что не определено четко, лишено определения и поэтому может быть полезным. Примеры опущены намеренно, так как я ожидаю, что они окажут большое влияние на ответы.   -  person Michael Gazonda    schedule 18.07.2014
comment
@MGaz: В вашем примере нет определения, это стандартное неопределенное поведение.   -  person Jon    schedule 18.07.2014
comment
@Jon - Есть пределы тому, что может произойти или не произойти. Таким образом, он находится где-то между определенным и неопределенным.   -  person Michael Gazonda    schedule 18.07.2014
comment
@MGaz: Нет, ограничений нет. Определение неопределенного поведения состоит в том, что может произойти что угодно.   -  person Jon    schedule 18.07.2014
comment
@Jon см. здесь некоторые обсуждения неясно, если это не определено в C++, но хорошо определено в C. На практике каламбур типов через объединение хорошо поддерживается.   -  person Shafik Yaghmour    schedule 18.07.2014
comment
Это не неопределенное поведение, потому что оно даже не компилируется.   -  person Brandin    schedule 18.07.2014
comment
@Brandin - исправлено, отсутствовала точка с запятой после объявления союза   -  person Michael Gazonda    schedule 18.07.2014
comment
Я до сих пор понятия не имею, что является четко определенным или неопределенным в этом контексте. Такого нет. Существует реализация, определенная, как указал @Jon. Это действительно то, о чем, я думаю, вы говорите здесь. Полагаться на поведение, определяемое реализацией, имеет смысл, если вы ориентируетесь на конкретную реализацию.   -  person Brandin    schedule 18.07.2014
comment
@Jon - Вы правы, что в неопределенном поведении может произойти что угодно. Я не согласен, что это подходит к этой категории. Это где-то посередине. Если вы можете доказать обратное, я весь в ушах.   -  person Michael Gazonda    schedule 18.07.2014
comment
Язык не указывает, что здесь должен делать компилятор, поэтому он не определен. Тем не менее, он будет давать стабильные результаты — независимо от порядка следования байтов на вашем компьютере! То есть только в пределах четко определенных граничных требований, например, этот код предназначен только для ЦП с обратным порядком байтов,   -  person Jongware    schedule 18.07.2014
comment
@MGaz - Нет, он все еще не компилируется. Возможно, вы имеете в виду union { ... } a = {23};, а затем пытаетесь ввести доступ к каламбуру a.b   -  person Brandin    schedule 18.07.2014
comment
@MGaz: откройте копию стандарта и прочитайте 3.10/10. В нем перечисляется ряд случаев, которые являются законными; все остальное является неопределенным поведением по определению. Включая (по крайней мере, для меня) приведенный выше код, потому что он не подходит ни для одного из юридических случаев.   -  person Jon    schedule 18.07.2014
comment
Добавлено большое обновление, надеюсь, вы снова откроете   -  person Michael Gazonda    schedule 18.07.2014
comment
Зайдите в начало ветки комментариев и прочитайте этот комментарий. Тогда посмотрите еще раз на свой вопрос. Какие из ваших примеров определены реализацией?   -  person Brandin    schedule 18.07.2014
comment
@MGaz: правило представляет собой простую тавтологию, хотя оно также есть в стандарте. Перефразируя: если стандарт не устанавливает никаких требований, он не устанавливает никаких требований.   -  person Deduplicator    schedule 18.07.2014
comment
@Jon: Псевдоним для массивов char является известным исключением из этого правила, IIRC.   -  person Lightness Races in Orbit    schedule 18.07.2014
comment
@Brandin - определенная реализация может быть ответом на мой вопрос, но не единственным ответом на вопрос.   -  person Michael Gazonda    schedule 18.07.2014
comment
@Deduplicator - есть места, где есть есть требования, хотя этих требований недостаточно, чтобы претендовать на четко определенные.   -  person Michael Gazonda    schedule 18.07.2014
comment
Рассмотрим эту программу int main() { int x; x *= 0; return x; }. Поведение этой программы явно не определено, но я был бы удивлен, обнаружив любую реализацию, которая не создает исполняемый файл, который всегда возвращает 0.   -  person Brandin    schedule 18.07.2014
comment
@brandin вы явно не встречали современный оптимизирующий компилятор.   -  person Flexo    schedule 18.07.2014
comment
@Flexo - что угодно * 0 = 0, как это могло привести к возврату чего-либо, кроме 0? x начинается как undefined, но undefined int * 0 должен быть равен 0. Нет случая, когда ответ может быть чем-то другим. Неопределенное значение != неопределенное поведение.   -  person Michael Gazonda    schedule 18.07.2014
comment
@Brandin: многие реализации оптимизируют умножение, возвращая весь мусор, который был в x перед функцией. Это может привести к сбою (см. Архитектура Itanium, NaT)   -  person Deduplicator    schedule 18.07.2014
comment
@Flexo Да, я полагаю, оптимизатор технически может воспользоваться неопределенным поведением и свободно оптимизировать тело функции. Я бы все равно удивился, найдя компилятор, выполняющий такую ​​оптимизацию в данном случае.   -  person Brandin    schedule 18.07.2014
comment
Будет ли оптимизация умножения ошибкой компилятора?   -  person Michael Gazonda    schedule 18.07.2014
comment
@MGaz Нет, это неправильно. Доступ к неинициализированной переменной является поведением undefined. Это означает, что реализация может делать все, что захочет. Может быть, я хочу, чтобы мой компилятор выдавал исключение, если вы это сделаете. Просто пример.   -  person Brandin    schedule 18.07.2014
comment
@Brandin, это правда, если вы думаете как математик, но это не те правила, по которым живут компиляторы. Современный компилятор может тривиально доказать, что использование переменной всегда неопределенно. Согласно правилу стандартов, это может и действительно допускает любой результат. Не только результаты, которые постулировал какой-то случайный человек в Интернете, имели бы смысл, но что угодно. Таким образом, компилятор может законно распространять эту неопределенность, чтобы делать то, что он считает оптимальным. См.: css.csail.mit.edu/stack для слегка искаженного представления о последствиях того, что.   -  person Flexo    schedule 18.07.2014
comment
Прочитайте связанный вопрос SO, который аналогичен тому, что вы подняли. Кстати, здесь никто не может дать правильный ответ, потому что ответ неопределен, как сейчас. Более того, мой мозг ведет себя неопределенно, если пытается прочитать или написать неопределенный ответ на вопрос, особенно при недосыпе. Спокойной ночи дамы/джентльмены. Я предлагаю перейти к некоторым реальным вопросам или спать.   -  person Brandin    schedule 18.07.2014
comment
@MGaz: stackoverflow.com/questions/11373203/ Запись в один член объединения с последующим чтением из другого запрещена разделом 9.5 стандарта C++. Это без какого-либо определения, потому что это не разрешено.   -  person Mooing Duck    schedule 18.07.2014
comment
@MooingDuck - прочитайте остальную часть ответа там.   -  person Michael Gazonda    schedule 18.07.2014
comment
@MGaz: Ответ №1: доступ без одного из вышеперечисленных [трюков/причуд] ​​— это неопределенное поведение. Ответ № 2: Если хранится только одно значение, как вы можете прочитать другое? Его просто нет. Ответ № 3: оба эти утверждения подразумевают, что просмотр (чтение) члена разрешен только в том случае, если 1) он является (частью) последнего записанного члена или 2) является частью общей исходной последовательности. (Это дает вам некоторое доверие) Ответ № 4: В этом случае это может быть неопределенное поведение из-за отсутствия спецификации. Ответ № 5: я не могу найти в стандарте ничего, что прямо запрещало бы это (это также поддерживает вас)   -  person Mooing Duck    schedule 18.07.2014
comment
@MGaz Как уже упоминалось, нет ничего где-то посередине. Разыменование nullptr — это неопределенное поведение, и точка.   -  person πάντα ῥεῖ    schedule 18.07.2014
comment
@πάντα ῥεῖ - Я специально сказал, что разыменование nullptr не определено. Не знаю, почему вы используете это в качестве аргумента против моего вопроса.   -  person Michael Gazonda    schedule 18.07.2014
comment
@ πάνταῥεῖ Мой лучше, хотя, строго говоря, ответ таков: избегайте их, когда это возможно, и убедитесь, что ваша реализация гарантирует выполнение того, что вы хотите, соответственно. вы в порядке с тем, как это может сделать любая реализация. Обильно вставляйте статические утверждения в свой код, проверяя эти предположения.   -  person Deduplicator    schedule 18.07.2014
comment
примечание: более подробная информация о различных типах поведения, stackoverflow.com/questions/2397984/   -  person M.M    schedule 18.07.2014
comment
Еще не дубликат. Речь идет о том, когда использовать неопределенное поведение, а не о том, что это такое.   -  person Michael Gazonda    schedule 18.07.2014


Ответы (1)


Чтобы ответить на новый вопрос: «Когда можно использовать неопределенное поведение?»

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

Например,

int foo() { cout << "foo"; return 1; }
int bar() { cout << "bar"; return 2; }
// ...
cout << (foo() + bar()) << "\n";

Если вам все равно, видите ли вы «foobar3» или «barfoo3», вы можете написать этот код. Если это имеет значение, вам придется изменить его, например.

int i = foo(); i += bar(); cout << i << "\n";

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

person M.M    schedule 17.07.2014
comment
В любое время для вас не имеет значения, какой вариант произойдет. Потрясающий! Я хочу исследовать это дальше. - person Michael Gazonda; 18.07.2014