const reference nullptr
От: Dair Россия  
Дата: 19.05.17 21:19
Оценка:
Коллеги,

Есть ли смысл в подобном коде:

const SomeClass& Class::getSomeClassVariable() const
{
    return *(SomeClass*)0;
}


Или это яркий пример так называемого говнокода, как мне и показалось с первого взгляда?
Re: const reference nullptr
От: MT-Wizard Украина  
Дата: 19.05.17 21:27
Оценка: +4
Здравствуйте, Dair, Вы писали:

D>Коллеги,


D>Есть ли смысл в подобном коде:


D>[/ccode]


D>Или это яркий пример так называемого говнокода, как мне и показалось с первого взгляда?


Это Undefined Behavior со всеми вытекающими. Но от лени его пишут — например, когда у SomeClass есть методы, не требующие this для работы или когда нет контроля над интерфейсом а NULL вернуть надо
Но всё равно UB
А ти, москалику, вже приїхав (с)
Re: const reference nullptr
От: jazzer Россия Skype: enerjazzer
Дата: 20.05.17 06:50
Оценка: -1
Здравствуйте, Dair, Вы писали:

D>Есть ли смысл в подобном коде


с С++11 — нет, так как для этого в Стандарте есть std::declval<const SomeClass>()

используется во всяких вычислениях типов, SFINAE, noexcept и т.д.
например:
decltype( std::declval<X>() + std::declval<Y>() )
noexcept( std::declval<X>().foo() + std::declval<Y>().bar() )
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: const reference nullptr
От: Evgeny.Panasyuk Россия  
Дата: 20.05.17 09:59
Оценка:
Здравствуйте, jazzer, Вы писали:

D>>Есть ли смысл в подобном коде

J>с С++11 — нет, так как для этого в Стандарте есть std::declval<const SomeClass>()

declval для случая ТС не подходит, у него другой контекст.
Re[3]: const reference nullptr
От: jazzer Россия Skype: enerjazzer
Дата: 20.05.17 11:43
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, jazzer, Вы писали:


D>>>Есть ли смысл в подобном коде

J>>с С++11 — нет, так как для этого в Стандарте есть std::declval<const SomeClass>()

EP>declval для случая ТС не подходит, у него другой контекст.


Ну он же не показал, как оно используется. У меня такая конструкция была только в местах, где сейчас надо использовать declval.
Хотя вру, еще она у меня использовалась, чтобы узнать смещение члена класса в объекте. Но сейчас offsetof тоже есть в Стандарте (правда там куча оговорок).
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: const reference nullptr
От: Evgeny.Panasyuk Россия  
Дата: 20.05.17 12:52
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ну он же не показал, как оно используется.


Тем не менее, если в том коде использовать declval — то программа не соберётся.

J>У меня такая конструкция была только в местах, где сейчас надо использовать declval.


Это да.

J>Но сейчас offsetof тоже есть в Стандарте (правда там куча оговорок).


offsetof есть начиная с C++98, а пришёл он ЕМНИП из C.
Re[5]: const reference nullptr
От: jazzer Россия Skype: enerjazzer
Дата: 20.05.17 13:30
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, jazzer, Вы писали:


J>>Ну он же не показал, как оно используется.


EP>Тем не менее, если в том коде использовать declval — то программа не соберётся.


В каком "в том"? там же нету кода, там есть функция, которая заменяется (с упомянутыми оговорками) на declval...


J>>Но сейчас offsetof тоже есть в Стандарте (правда там куча оговорок).


EP>offsetof есть начиная с C++98, а пришёл он ЕМНИП из C.


но он только для standard layout и всякое такое, там куча ограничений. А хак с нулевым указателем будет работать везде (и на древних компиляторах тоже)
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: const reference nullptr
От: Evgeny.Panasyuk Россия  
Дата: 20.05.17 13:41
Оценка:
Здравствуйте, jazzer, Вы писали:

J>>>Ну он же не показал, как оно используется.

EP>>Тем не менее, если в том коде использовать declval — то программа не соберётся.
J>В каком "в том"? там же нету кода, там есть функция, которая заменяется (с упомянутыми оговорками) на declval...

А, ты о том чтобы заменить весь метод declval'ом. В таком случае, если там действительно declval был бы уместен то и тело метода в изначальном варианте было бы не нужно (достаточно было бы декларации) — но это уже всё размышление на тему "что хотел сказать автор".
Re[2]: const reference nullptr
От: Evgeny.Panasyuk Россия  
Дата: 20.05.17 13:54
Оценка: 2 (2)
Здравствуйте, MT-Wizard, Вы писали:

MW>или когда нет контроля над интерфейсом а NULL вернуть надо


Главное потом аккуратно это всё использовать, ибо UB. Некоторое время назад ловил баг в сторонней библиотеке связанный как раз с этим — в ссылках передавался nullptr, и в одном из мест был тест ссылки на этот самый nullptr, который компилятор безусловно выкидывал пользуясь правом UB, и код ниже соответственно падал понадеявшись на проверку.
Re[7]: const reference nullptr
От: jazzer Россия Skype: enerjazzer
Дата: 22.05.17 12:31
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>А, ты о том чтобы заменить весь метод declval'ом.


Да, именно.
Как-то даже и в голову не пришло, что это кто-то собирается вызывать в рантайме
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: const reference nullptr
От: Кодт Россия  
Дата: 22.05.17 12:58
Оценка: 13 (3)
Здравствуйте, Dair, Вы писали:

D>Или это яркий пример так называемого говнокода, как мне и показалось с первого взгляда?


Без контекста непонятно, до какой степени это говнокод.
Но говнокод в любом случае, т.к. это эксплуатация неопределённого поведения.

Перечень бомб, которые здесь заложены:
— оптимизатор может бодро повыкидывать куски рабочего кода
— AV, если вдруг потребуется разыменовать указатель, — например, какая-то функция из невиртуальной будет переделана в виртуальную, или если оптимизатор заменит статический вызов виртуальной функции на динамический
— ненулевой адрес базы/наследника при сдвиге базы (при работе с указателями создаётся специальный код, превращающий нуль в нуль; при работе со ссылками этих проверок нет)
— последующее AV при работе с ненулевыми же адресами!
— в параноидном режиме программа просто не скомпилируется
Перекуём баги на фичи!
Re[2]: const reference nullptr
От: Dair Россия  
Дата: 22.05.17 14:25
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Без контекста непонятно, до какой степени это говнокод.

Контекста я не застал, потому что [см.ниже]
Посмотрел по коду немного — это заглушка виртуальной функции.

К>- в параноидном режиме программа просто не скомпилируется

есть у нас либА, которая делается в соседнем отделе. Эта либа перестала собираться с обновлением Xcode (и clang, видимо), именно на вот этом месте.

Коллеги переделали функцию, теперь она возвращает указатель, а не константную ссылку.
Re[3]: const reference nullptr
От: Кодт Россия  
Дата: 22.05.17 14:32
Оценка:
Здравствуйте, Dair, Вы писали:

К>>- в параноидном режиме программа просто не скомпилируется

D>есть у нас либА, которая делается в соседнем отделе. Эта либа перестала собираться с обновлением Xcode (и clang, видимо), именно на вот этом месте.

Вот за это я люблю шланг! Хотя, местами, он параноит больше, чем надо. Но эта паранойя регулируется.

D>Коллеги переделали функцию, теперь она возвращает указатель, а не константную ссылку.


Ага, и перенесли эпицентр взрыва с места пуска в место попадания. Ма-лай-цы.
Срочно скажи им, чтобы засунули в заглушку ассерт. И/или написали в документации, что виртуальная функция может возвращать нуль.
Перекуём баги на фичи!
Re[4]: const reference nullptr
От: Dair Россия  
Дата: 22.05.17 15:17
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Срочно скажи им, чтобы засунули в заглушку ассерт. И/или написали в документации, что виртуальная функция может возвращать нуль.

А вот просто написать virtual someFunction() == 0, не?
Re[5]: const reference nullptr
От: Кодт Россия  
Дата: 22.05.17 18:27
Оценка:
Здравствуйте, Dair, Вы писали:

D>А вот просто написать virtual someFunction() == 0, не?


Это же не заглушка, не?
Перекуём баги на фичи!
Re[6]: const reference nullptr
От: Dair Россия  
Дата: 22.05.17 21:07
Оценка:
Здравствуйте, Кодт, Вы писали:

D>>А вот просто написать virtual someFunction() == 0, не?

К>Это же не заглушка, не?

я уточню в офисе

Я проверю в коде. Чисто из любопытства. Завтра отпишусь.
Re: const reference nullptr
От: MasterZiv СССР  
Дата: 25.05.17 08:50
Оценка: +2
Здравствуйте, Dair, Вы писали:

D>
D>const SomeClass& Class::getSomeClassVariable() const
D>{
D>    return *(SomeClass*)0;
D>}
D>


D>Или это яркий пример так называемого говнокода, как мне и показалось с первого взгляда?


Это -- яркий пример говнокода.
И мне стыдно, но я когда-то такой код писал сам

НАДО КИДАТЬ ИСКЛЮЧЕНИЕ!
Re[2]: const reference nullptr
От: MNZ Россия  
Дата: 29.05.17 09:30
Оценка: +1 -1
Здравствуйте, MasterZiv, Вы писали:

MZ>Здравствуйте, Dair, Вы писали:


D>>
D>>const SomeClass& Class::getSomeClassVariable() const
D>>{
D>>    return *(SomeClass*)0;
D>>}
D>>


D>>Или это яркий пример так называемого говнокода, как мне и показалось с первого взгляда?


MZ>Это -- яркий пример говнокода.

MZ>И мне стыдно, но я когда-то такой код писал сам

MZ>НАДО КИДАТЬ ИСКЛЮЧЕНИЕ!


Нет, не надо Исключение надо бросать тогда, когда ошибку невозможно исправить после внимательного прочтения кода. То есть, когда ошибка вызвана какими-то внешними факторами. Здесь же будет достаточно ассерта или что-то в этом роде.
Re[3]: const reference nullptr
От: N. I.  
Дата: 31.05.17 09:16
Оценка:
MNZ:

MZ>>НАДО КИДАТЬ ИСКЛЮЧЕНИЕ!


MNZ>Нет, не надо Исключение надо бросать тогда, когда ошибку невозможно исправить после внимательного прочтения кода. То есть, когда ошибка вызвана какими-то внешними факторами. Здесь же будет достаточно ассерта или что-то в этом роде.


Прально, std::terminate рулит
Re[2]: const reference nullptr
От: ομικρον  
Дата: 27.06.17 16:15
Оценка:
Здравствуйте, Кодт, Вы писали:

К>- ненулевой адрес базы/наследника при сдвиге базы (при работе с указателями создаётся специальный код, превращающий нуль в нуль; при работе со ссылками этих проверок нет)


Вроде как и для ссылок есть проверки. Правда, говорят, не во всех случаях
Автор: Кодт
Дата: 28.10.09
.
Re[3]: const reference nullptr
От: Кодт Россия  
Дата: 27.06.17 16:31
Оценка:
Здравствуйте, ομικρον, Вы писали:

К>>- ненулевой адрес базы/наследника при сдвиге базы (при работе с указателями создаётся специальный код, превращающий нуль в нуль; при работе со ссылками этих проверок нет)


ομι>Вроде как и для ссылок есть проверки. Правда, говорят, не во всех случаях
Автор: Кодт
Дата: 28.10.09
.


http://ideone.com/A1mFYA

Если компилятор сам догадается, что происходит разыменование нуля, он может нагенерить любую ахинею.
Если не догадается, то просто молча сделает сдвиг базы.
Перекуём баги на фичи!
Re[7]: const reference nullptr
От: IID Россия  
Дата: 11.07.17 18:40
Оценка: :)
Здравствуйте, Dair, Вы писали:

D>я уточню в офисе


D>Я проверю в коде. Чисто из любопытства. Завтра отпишусь.


Ай-яй.
Мне, конечно, неинтересно, но раз пообещал — выполняй
kalsarikännit
Re[2]: const reference nullptr
От: prezident.mira Россия  
Дата: 11.07.17 19:03
Оценка: -1
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Dair, Вы писали:


D>>Или это яркий пример так называемого говнокода, как мне и показалось с первого взгляда?


К>Без контекста непонятно, до какой степени это говнокод.

К>Но говнокод в любом случае, т.к. это эксплуатация неопределённого поведения.

А есть ли здесь UB? Т.к. просто применение оператора * к нулевому указателю UB не является (несмотря на широко распространённое поверие, что является). Т.е.
*(SomeClass*)nullptr;

это ещё не UB само по себе.

UB является инициализация ссылок с помощью нулевой ссылки, вроде
const SomeClass& ref = *(SomeClass*)nullptr;


А вот является ли UB просто возврат нулевой ссылки из функции?

UPD: нашёл сам: return является инициализацией. Значит UB.
Отредактировано 11.07.2017 19:15 prezident.mira . Предыдущая версия .
Re[8]: const reference nullptr
От: Dair Россия  
Дата: 11.07.17 19:23
Оценка: 1 (1)
Здравствуйте, IID, Вы писали:

D>>Я проверю в коде. Чисто из любопытства. Завтра отпишусь.

IID>Мне, конечно, неинтересно, но раз пообещал — выполняй

В самом деле, забыл.

Заглушка, метод переопределен во всех "реальных" дочерних классах.
Re[3]: const reference nullptr
От: uzhas Ниоткуда  
Дата: 12.07.17 08:27
Оценка:
Здравствуйте, prezident.mira, Вы писали:

PM>А есть ли здесь UB? Т.к. просто применение оператора * к нулевому указателю UB не является (несмотря на широко распространённое поверие, что является).


я придерживаюсь противоположной точки зрения. можно ознакомиться здесь: https://stackoverflow.com/questions/28573215/is-dereferencing-a-pointer-thats-equal-to-nullptr-undefined-behavior-by-the-sta
и в C и в C++ разыменование нулевого указателя — UB имхо
однако считаю, что стандарту следует быть более точным в формулировках относительно данного вопроса
Re[3]: const reference nullptr
От: uzhas Ниоткуда  
Дата: 12.07.17 08:32
Оценка:
Здравствуйте, prezident.mira, Вы писали:

PM>UPD: нашёл сам: return является инициализацией.


Re[4]: const reference nullptr
От: prezident.mira Россия  
Дата: 12.07.17 17:57
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, prezident.mira, Вы писали:


PM>>А есть ли здесь UB? Т.к. просто применение оператора * к нулевому указателю UB не является (несмотря на широко распространённое поверие, что является).


U>я придерживаюсь противоположной точки зрения. можно ознакомиться здесь: https://stackoverflow.com/questions/28573215/is-dereferencing-a-pointer-thats-equal-to-nullptr-undefined-behavior-by-the-sta

Я ознакомился со ссылкой. Notes from the October 2003 meeting: [...] We agreed that the approach in the standard seems okay: p = 0; *p; is not inherently an error.

U>и в C и в C++ разыменование нулевого указателя — UB имхо

К счастью, что есть UB — определяется стандартом, а не чьими-то имхами.
Re[4]: const reference nullptr
От: prezident.mira Россия  
Дата: 12.07.17 17:59
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, prezident.mira, Вы писали:


PM>>UPD: нашёл сам: return является инициализацией.


U>


Это типа ты не согласен?
Re[5]: const reference nullptr
От: N. I.  
Дата: 15.07.17 12:56
Оценка:
prezident.mira:

U>>и в C и в C++ разыменование нулевого указателя — UB имхо

PM>К счастью, что есть UB — определяется стандартом, а не чьими-то имхами.

Ну, и какое же behavior для вычисляющегося выражения вроде *(int*)nullptr определяется действующим стандартом C++?

C++14 [expr.unary.op]

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.


(int*)nullptr не указывает на какой-либо объект, и, следовательно, результатом здесь не может быть "an lvalue referring to the object or function to which the expression points". Если результат выполнения этого выражения другой, то какой именно? Если описания поведения нет, то де-юре получаем не что иное, как undefined behavior:

C++14 [defns.undefined]

undefined behavior
behavior for which this International Standard imposes no requirements
[ Note: Undefined behavior may be expected when this International Standard omits any explicit definition of
behavior ....


Де-факто же *(int*)nullptr считается просто no-op, т.е. это один из тех случаев, когда следует руководствоваться _мнением_ отдельных представителей комитета по стандартизации, а не официальным правилам.

Можно привести ещё более интересный пример, где по правилам C++ undefined behavior возникает де-юре:

#include <cstdlib>

int main()
{
    void *p = std::malloc(sizeof(int));
    *(int *)p; // undefined behavior
    std::free(p);
}

Причина та же самая, что и в случае с нулевым указателем: по адресу p нет никаких объектов. Де-факто подразумевается, что такая конструкция должна работать примерно как если бы вызов std::malloc(sizeof(int)) создал неинициализированный объект типа int.
Re[6]: const reference nullptr
От: prezident.mira Россия  
Дата: 15.07.17 14:04
Оценка:
Здравствуйте, N. I., Вы писали:

NI>C++14 [expr.unary.op]

NI>

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.


NI>(int*)nullptr не указывает на какой-либо объект, и, следовательно, результатом здесь не может быть "an lvalue referring to the object or function to which the expression points". Если результат выполнения этого выражения другой, то какой именно? Если описания поведения нет, то де-юре получаем не что иное, как undefined behavior:


Скажите, тип указателя тут важен? Ну т.е. важно, получаем ли мы null pointer to int или на какой ещё тип? (incomplete types не рассматриваем).
Re[6]: const reference nullptr
От: prezident.mira Россия  
Дата: 15.07.17 14:20
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Можно привести ещё более интересный пример, где по правилам C++ undefined behavior возникает де-юре:


NI>
#include <cstdlib>

NI>int main()
NI>{
NI>    void *p = std::malloc(sizeof(int));
NI>    *(int *)p; // undefined behavior
NI>    std::free(p);
NI>}

NI>Причина та же самая, что и в случае с нулевым указателем: по адресу p нет никаких объектов. Де-факто подразумевается, что такая конструкция должна работать примерно как если бы вызов std::malloc(sizeof(int)) создал неинициализированный объект типа int.

http://eel.is/c++draft/basic.life#6

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a pointer refers to allocated storage ([basic.stc.dynamic.deallocation]), and using the pointer as if the pointer were of type void*, is well-defined. Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below.


Если expression это lvalue, которое referring to/denoting объект — этот объект "существовать" или быть ещё каким-то образом "валидным" не обязан, кроме как в специально оговорённых случаях.
Следует различать "lvalue, denoting object" и сам "object".

Сюда же пункт 7 http://eel.is/c++draft/basic.life#7

Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.deallocation]), and using the properties of the glvalue that do not depend on its value is well-defined.

Отредактировано 15.07.2017 14:25 prezident.mira . Предыдущая версия .
Re[7]: const reference nullptr
От: N. I.  
Дата: 15.07.17 16:30
Оценка:
prezident.mira:

NI>>(int*)nullptr не указывает на какой-либо объект, и, следовательно, результатом здесь не может быть "an lvalue referring to the object or function to which the expression points". Если результат выполнения этого выражения другой, то какой именно? Если описания поведения нет, то де-юре получаем не что иное, как undefined behavior:


PM>Скажите, тип указателя тут важен? Ну т.е. важно, получаем ли мы null pointer to int или на какой ещё тип? (incomplete types не рассматриваем).


Если *(T *)nullptr вычисляется, будучи операндом typeid (что возможно только если T — полиморфный тип), то поведение является определённым: typeid должен бросить исключение — этот случай специально оговорён. В остальных случаях, если T — это object/function type, то насколько я вижу, при вычислении *(T *)nullptr де-юре получается undefined behavior.
Отредактировано 15.07.2017 16:39 N. I. . Предыдущая версия .
Re[7]: const reference nullptr
От: N. I.  
Дата: 15.07.17 16:32
Оценка:
prezident.mira:

PM>

PM>Before the lifetime of an object has started but after the storage which the object will occupy has been allocated


А если никаких попыток создать объект вообще не предпринимается, это относится к "Before the lifetime of an object has started" или нет? Без авторитетного толкователя это правило можно интерпретировать как хочешь. Впрочем, если пример заменить на

#include <stdlib.h>

struct X
{
    int n;
};

int main()
{
    X *p = (X *)malloc(sizeof(X));
    p->n = 1; // undefined behavior
    free(p);
}

то тут хоть как трактуй, получишь undefined behavior — либо из-за indirection, либо из-за попытки "to access a non-static data member or call a non-static member function of the object", что весьма доставляет, т.к. подобный код широко используется в C.
Отредактировано 15.07.2017 16:34 N. I. . Предыдущая версия .
Re[8]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 00:18
Оценка:
Здравствуйте, N. I., Вы писали:

NI>prezident.mira:


NI>>>(int*)nullptr не указывает на какой-либо объект, и, следовательно, результатом здесь не может быть "an lvalue referring to the object or function to which the expression points". Если результат выполнения этого выражения другой, то какой именно? Если описания поведения нет, то де-юре получаем не что иное, как undefined behavior:


PM>>Скажите, тип указателя тут важен? Ну т.е. важно, получаем ли мы null pointer to int или на какой ещё тип? (incomplete types не рассматриваем).


NI>Если *(T *)nullptr вычисляется, будучи операндом typeid (что возможно только если T — полиморфный тип), то поведение является определённым: typeid должен бросить исключение — этот случай специально оговорён. В остальных случаях, если T — это object/function type, то насколько я вижу, при вычислении *(T *)nullptr де-юре получается undefined behavior.


Т.е. по-Вашему полиморфные объекты существуют по нулевому указателю.
Re[8]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 00:51
Оценка:
Здравствуйте, N. I., Вы писали:

NI>prezident.mira:


PM>>

PM>>Before the lifetime of an object has started but after the storage which the object will occupy has been allocated


NI>А если никаких попыток создать объект вообще не предпринимается, это относится к "Before the lifetime of an object has started" или нет?


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

NI> Без авторитетного толкователя это правило можно интерпретировать как хочешь.


Да, я понял. Пока не прилетит Страуструп на голубом вертолёте и не скажет: "N.I.! INDIRECTION ЧЕРЕЗ НУЛЕВОЙ УКАЗАТЕЛЬ — ЭТО НЕ UB!" то Вы всё что угодно будете интерпретировать как Вам угодно, лишь бы показаться правым во что бы то ни стало.

А по факту http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#315 закрыто как NAD (Not A Defect), что значит, что

The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.

и в Rationale у 315 написано

*p is not an error when p is null unless the lvalue is converted to an rvalue


NI> тут хоть как трактуй, получишь undefined behavior


Если трактовать как Вам угодно, то UB будет всё, что Вам угодно. А остальным стандарт говорит http://eel.is/c++draft/basic.life#1

The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. The lifetime of an object of type T begins when:

Т.е. тут не UB. Кстати, к предыдущему примеру кода с "UB" этот пункт стандарта тоже относится.

NI> что весьма доставляет, т.к. подобный код широко используется в C.


В C нет конструкторов, там lifetime начинается с обретением storage. И, как видно из приведённого выше пункта стандарта, C++ тут совместим с C в случае тривиальных конструкторов.
Отредактировано 16.07.2017 4:34 prezident.mira . Предыдущая версия . Еще …
Отредактировано 16.07.2017 4:33 prezident.mira . Предыдущая версия .
Re[9]: const reference nullptr
От: N. I.  
Дата: 16.07.17 09:13
Оценка:
prezident.mira:

NI>>Если *(T *)nullptr вычисляется, будучи операндом typeid (что возможно только если T — полиморфный тип), то поведение является определённым: typeid должен бросить исключение — этот случай специально оговорён. В остальных случаях, если T — это object/function type, то насколько я вижу, при вычислении *(T *)nullptr де-юре получается undefined behavior.


PM>Т.е. по-Вашему полиморфные объекты существуют по нулевому указателю.


Нет, это особый случай вычисления *(T *)nullptr из серии "игнорируйте, что мы писали вон там, и руководствуйтесь описанием поведения именно для данного случая". Утверждение, что для indirection "the result is an lvalue referring to the object or function to which the expression points" в данном случае, очевидно, является некорректным, но зато поведение всей конструкции typeid(*(T *)nullptr) в целом является документированным.
Re[9]: const reference nullptr
От: N. I.  
Дата: 16.07.17 09:23
Оценка: 4 (2)
prezident.mira:

NI>>А если никаких попыток создать объект вообще не предпринимается, это относится к "Before the lifetime of an object has started" или нет?


PM>Это уже попытка придраться на пустом месте. Вам показали, что Вы не правы, Вам стало от этого неприятно и Вы теперь цепляетесь за соломинку.

PM>Там всё ясно написано и оракулов, предсказывающих будущее, для описания текущего поведения стандарт не требует.

"Всё ясно" там может быть только тем, кто плохо представляет, как должны выглядеть строгие формальные правила. Не всякий указатель типа T*, указывающий на некую область памяти, где расположен объект типа T, может использоваться для доступа к этому объекту в течение его lifetime. В C++17 даже появилось специальное средство std::launder для "отмывания" указателей:

[ptr.launder]

struct X { const int n; };
X *p = new X{3};
const int a = p->n;
new (p) X{5}; // p does not point to new object (3.8)
// because X::n is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // OK


То, что здесь помечено как undefined behavior — это не какое-то новшество C++17, в C++14 оно также подразумевается. Раз уж между объектом и указателем на него может быть такая хитрая связь, то почему бы не допустить, что в формулировке "Before the lifetime of an object has started" речь также идёт о какой-то привязке к объекту, который реально пытаются создать? Если мы не обязаны ничего создавать, то почему бы вместо этой галиматьи

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways.


не использовать что-то вроде

Any pointer of type cv1 T* that represents the address of a storage location suitable to hold an object of type T, but does not point to any object of type cv2 T whose lifetime has started and not ended, may be used but only in limited ways.


NI>> Без авторитетного толкователя это правило можно интерпретировать как хочешь.


PM>Да, я понял. Пока не прилетит Страуструп на голубом вертолёте и не скажет: "N.I.! INDIRECTION ЧЕРЕЗ НУЛЕВОЙ УКАЗАТЕЛЬ — ЭТО НЕ UB!" то Вы всё что угодно будете интерпретировать как Вам угодно, лишь бы показаться правым во что бы то ни стало.


В таких вопросах я бы ему не стал доверять :-) Вот таким людям, как Майк Миллер, Йенс Маурер и Ричард Смит, я верю.

PM>А по факту http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#315 закрыто как NAD (Not A Defect), что значит, что

PM>

PM>The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.


И что? CWG решила, что разыменование нулевого указателя должно быть разрешено, но официального (закреплённого в стандарте) описания поведения программы при таком разыменовании (кроме специального случая с typeid) по-прежнему нет.

NI>> тут хоть как трактуй, получишь undefined behavior


PM>Если трактовать как Вам угодно, то UB будет всё, что Вам угодно. А остальным стандарт говорит http://eel.is/c++draft/basic.life#1

PM>

PM>The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. The lifetime of an object of type T begins when:
PM>

    PM>
  • storage with the proper alignment and size for type T is obtained, and
    PM>
  • if the object has non-vacuous initialization, its initialization is complete,
    PM>


Кучка байтов из памяти не может просто так стать объектом. "An object is created by a definition (3.1), by a new-expression (5.3.4) or by the implementation (12.2) when needed." Если ничто в стандарте не говорит, что в некоем storage у тебя создаётся какой-то объект, то о существовании там такого объекта говорить нельзя. malloc объектов не создаёт:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0137r1.html

Drafting note: this maintains the status quo that malloc alone is not sufficient to create an object.


PM>В C нет конструкторов, там lifetime начинается с обретением storage. И, как видно из приведённого выше пункта стандарта, C++ тут совместим с C в случае тривиальных конструкторов.


Этот пункт не имеет отношения к определению множества конструкций, которые в принципе способны создавать объекты. Похожий пример сравнительно недавно рассматривался на core рефлекторе комитета по стандартизации, вот часть обсуждения:

  Скрытый текст
Peter Dimov:
All C code in existence is dead, it just doesn't know it yet.

Bjarne Stroustrup:
Interesting. Any details? :-)

Peter Dimov:
Well as I already said,

   struct X
   {
       int a, b;
   };

   int main()
   {
       X* p = (X*)malloc( sizeof(X) );

       p->b = 1; // undefined behavior
   }


Richard Smith:
One of my main goals in writing P0137 was to clarify the existing language rules. We cannot know whether the language rules express our intent without knowledge or agreement of what the language rules even *are*. Prior to P0137, asking N language lawyers about the above example would have given you probably more than 2N different answers, due the the confused and self-contradictory wording around the description of objects and lifetime. It was never my intent to *stop* after P0137; rather, this was intended to be the starting point for refining the object model to the point where it guarantees some reasonable subset of the implementation details that users have been relying on, while not infringing too heavily on optimizability (that users have *also* been relying on).

Now we have a suitably precise framework in order to express questions such as the above and reason about their answers, we can think about exactly which cases we want to have defined behavior, and which cases we want to allow implementations to (let their users) choose between bug detection / optimization and supporting low-level memory manipulation. I'm generally of the opinion that we should require users to be explicit when doing the latter, but making exceptions for some grandfathered-in C compatibility cases should be considered.

For instance, I strongly believe the above example *should* be made to have defined behavior in C++. But no ISO C++ standard has ever guaranteed it to have defined behavior -- an implementation is, and always was, at liberty to claim that it violates [basic.lval]/8, because the user never started the lifetime of an 'int' object to store p->b.
Отредактировано 16.07.2017 9:54 N. I. . Предыдущая версия .
Re[10]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 09:59
Оценка:
Здравствуйте, N. I., Вы писали:

NI>prezident.mira:


NI>>>Если *(T *)nullptr вычисляется, будучи операндом typeid (что возможно только если T — полиморфный тип), то поведение является определённым: typeid должен бросить исключение — этот случай специально оговорён. В остальных случаях, если T — это object/function type, то насколько я вижу, при вычислении *(T *)nullptr де-юре получается undefined behavior.


PM>>Т.е. по-Вашему полиморфные объекты существуют по нулевому указателю.


NI>Нет, это особый случай вычисления *(T *)nullptr из серии "игнорируйте, что мы писали вон там, и руководствуйтесь описанием поведения именно для данного случая".


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

If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted

про оператор typeid говорится, что

glvalue expression is obtained by applying the unary * operator to a pointer

typeid не отменяет вычисления подвыражения вида *p и, если бы оно само по себе приводило к UB, то всё выражение с ним, как и остальное поведение программы стало бы undefined.

Причина, по которой typeid оговаривает поведение, проста: про полиморфные объекты стандарт говорит

Some objects are polymorphic; the implementation generates information associated with
each such object that makes it possible to determine that object’s type during program execution. For other
objects, the interpretation of the values found therein is determined by the type of the expressions
used to access them.

Т.е. для определения типа полиморфного объекта может понадобиться обращение к его значению. И хотя indirection для нулевого указателя это не UB, но попытка чтения значения — UB. Поэтому typeid, применение которого к объекту полиморфного типа потенциально может привести к чтению значения, явно оговаривает, что в случае lvalue, полученного разыменованием нулевого указателя, его применение к UB не приведёт.

NI> Утверждение, что для indirection "the result is an lvalue referring to the object or function to which the expression points" в данном случае, очевидно, является некорректным


Оно является контринтуитивным. И проблема только в этом — в wording-е. По этому поводу открыт issue 232. Где видно, что в корректности indirection для нулевого указателя и указателя, ссылающегося на элемент за последним элементом массива сомнений нет. А нужно изменить wording для этих случаев.
Re[10]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 10:36
Оценка:
Здравствуйте, N. I., Вы писали:

NI>И что? CWG решила, что разыменование нулевого указателя должно быть разрешено, но официального (закреплённого в стандарте) описания поведения программы при таком разыменовании (кроме специального случая с typeid) по-прежнему нет.


CWG решила, что дефекта в стандарте нет, поэтому менять в нём ничего не нужно. А что касается Вашего любимого правила "behavior for which this International Standard imposes no requirements" которое вы применяете как-то своеобразно, то результат применения унарного оператора * определён:

the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points


Написано: "shall be a pointer to an object type,". Т.е. говорится про тип выражения.
Я бы понял Ваши постоянные отсылки к определению UB, если бы было сказано "shall be a pointer to an object,". Но такого требования нет. Про значения указателя ничего не сказано — значит валидно для всех значений. Или Вы, может быть, считаете, что пока для всех пар значений целых чисел в стандарте явно не будет определён результат сложения (если нет переполнения), то 1+1 будет приводить к UB?

Требования к входному выражению предъявлены, результат описан. А что описан контринтуитивным для некоторых значений wording-ом — это не мешает ему быть определённым.
Re[10]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 11:04
Оценка:
Здравствуйте, N. I., Вы писали:

NI>"Всё ясно" там может быть только тем, кто плохо представляет, как должны выглядеть строгие формальные правила.


Ок. Результат вычисления 1+1 это UB, пока в стандарте строго не сказано что это 2.

NI>Кучка байтов из памяти не может просто так стать объектом. "An object is created by a definition (3.1), by a new-expression (5.3.4) or by the implementation (12.2) when needed."


Да. Выделили malloc-ом "storage with the proper alignment and size for type T" и, если тип "have non-vacuous initialization", вызвали конструктор с использованием placement new. Всё соответствует тому, что вы процитировали и http://eel.is/c++draft/basic.life#1

NI> malloc объектов не создаёт:

NI>http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0137r1.html
NI>

Drafting note: this maintains the status quo that malloc alone is not sufficient to create an object.


Я уже не в первый раз замечаю, что Вы не особо чистоплотны в методах ведения дискуссии. Либо просто не умеете читать.
Написано "malloc alone is not sufficient to create an object" — "только malloc-а недостаточно для создания [всякого] объекта".
Вы это подменили на "malloc объектов не создаёт". Чего в этой фразе не говорится.
Да, malloc-а недостаточно для создания всякого объекта, т.к. для некоторых типов кроме выделения стораджа может быть необходимо вызвать нетривиальный конструктор. См. http://eel.is/c++draft/basic.life#1 опять же.

PM>>В C нет конструкторов, там lifetime начинается с обретением storage. И, как видно из приведённого выше пункта стандарта, C++ тут совместим с C в случае тривиальных конструкторов.


NI>Этот пункт не имеет отношения к определению множества конструкций, которые в принципе способны создавать объекты. Похожий пример сравнительно недавно рассматривался на core рефлекторе комитета по стандартизации, вот часть обсуждения:


Больше похоже на то, что участники забыли про существование http://eel.is/c++draft/basic.life#1
Re[10]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 11:50
Оценка:
NI>И что? CWG решила, что разыменование нулевого указателя должно быть разрешено, но официального (закреплённого в стандарте) описания поведения программы при таком разыменовании (кроме специального случая с typeid) по-прежнему нет.

Смотрим, что написано по ссылке http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html

This document contains the C++ core language issues for which the Committee (J16 + WG21) has decided that no action is required

Ещё раз смотрим в описание статуса NAD:

The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.


О чём говорит issue №315, про который сказано, что это not a defect? Про то, что про обращение к не-статической функции-члену класса через нулевой указатель явно сказано, что оно undefined, а про обращение к статической не сказано undefined, но, автор issue считает что должно быть сказано

If f is static, however, there seems to be no such rule, and the call is only undefined if the dereference implicit in the -> operator is undefined. IMO it should be.

На что ему отвечают, что нет, не должно быть undefined, т.к.

*p is not an error when p is null unless the lvalue is converted to an rvalue



Так что не врите про "CWG решила, что разыменование нулевого указателя должно быть разрешено".
No action по внесению изменений, говорящих об UB в случае доступа через нулевой указатель к статической функции, is required, потому что отсутствие этого в стандарте not a defect, т.к. просто разыменование нулевого указателя, которое происходит при вычислении выражения вида E1.E2, где E2 обозначает статический член, а E1 это, собственно, разыменование нулевого указателя, разрешено. Именно "разрешено", а не "должно быть разрешено".
Отредактировано 16.07.2017 11:59 prezident.mira . Предыдущая версия .
Re[10]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 12:22
Оценка:
Здравствуйте, N. I., Вы писали:

NI>В таких вопросах я бы ему не стал доверять Вот таким людям, как Майк Миллер, Йенс Маурер и Ричард Смит, я верю.


Ну вот Вам Ричард Смит https://groups.google.com/a/isocpp.org/d/msg/std-discussion/hG1Qx8a6Wjw/Q7aLfxfGAgAJ

The difference is that in core issue 315, the member function is a static member function.

This makes more sense if you think of non-static member functions as being passed an implied first parameter that is a reference to the class type. (The fact that 'this' is a pointer rather than a reference is a historical accident.) Just like any other reference parameter, it can't be bound to a dereferenced null pointer. But a static member function has no such parameter; the lvalue expression denoting the object is evaluated and then discarded, and null ("empty") lvalues are OK so long as you don't bind a reference to them or try to access an object through them.


Ещё вопросы есть, или в ответ опять будет "И что?" и тонна бла-бла вида "ну это смотря как это понимать"?
Re[11]: const reference nullptr
От: N. I.  
Дата: 16.07.17 13:19
Оценка:
prezident.mira:

NI>>Нет, это особый случай вычисления *(T *)nullptr из серии "игнорируйте, что мы писали вон там, и руководствуйтесь описанием поведения именно для данного случая".


PM>typeid не отменяет вычисления подвыражения вида *p и, если бы оно само по себе приводило к UB, то всё выражение с ним, как и остальное поведение программы стало бы undefined.


typeid(*p) — это специальная конструкция, в отношении которой действуют особые правила, и поведение выражения *p определяется контекстом его использования. Кстати, выражение *p, значение которого нигде не используется, — это тоже специальный случай. Например, здесь

#include <typeinfo>

struct Polymorphic
{
    virtual ~Polymorphic() {}
};

int main()
{
    Polymorphic volatile *p = nullptr;
    *p;         // undefined behavior
    typeid(*p); // well-defined
}

к обособленному *p применяется lvalue-to-rvalue conversion, потому что это volatile-qualified lvalue, и даже введение понятия empty lvalue от undefined behavior тут уже не спасёт. В случае с typeid(*p) такого lvalue-to-rvalue conversion не будет.
Re[11]: const reference nullptr
От: N. I.  
Дата: 16.07.17 13:22
Оценка:
prezident.mira:

NI>>И что? CWG решила, что разыменование нулевого указателя должно быть разрешено, но официального (закреплённого в стандарте) описания поведения программы при таком разыменовании (кроме специального случая с typeid) по-прежнему нет.


PM>CWG решила, что дефекта в стандарте нет, поэтому менять в нём ничего не нужно.


В каком конкретно месте его нет? Issue 232 уже закрыли?

PM>А что касается Вашего любимого правила "behavior for which this International Standard imposes no requirements" которое вы применяете как-то своеобразно, то результат применения унарного оператора * определён:

PM>

PM>the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points


Ещё раз: что будет результатом выражения *p, где p — нулевой указатель? "Не пойми что"? И что я дальше могу делать с этим "не пойми что"?

PM>Написано: "shall be a pointer to an object type,". Т.е. говорится про тип выражения.

PM>Я бы понял Ваши постоянные отсылки к определению UB, если бы было сказано "shall be a pointer to an object,".

Мои отсылки делались именно к отсутствию явного определения поведения в таком случае.

PM>Про значения указателя ничего не сказано — значит валидно для всех значений.


Нет, не для всех. Применение indirection к invalid pointer value — это явное undefined behavior:

[basic.stc.dynamic.deallocation]/4:

Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior.


Null pointer value не является invalid pointer value, поэтому в случае с разыменованием null pointer неопределённое поведение — это только следствие отсутствия явно определённого поведения.

PM>Требования к входному выражению предъявлены, результат описан.


Результат, которого там на самом деле не может быть? И что я могу делать с этим результатом? И какие правила при этом должен применять? Например, я захотел использовать *(int volatile *)nullptr в качестве discarded-value-expression. В какие пункты стандарта мне смотреть, чтоб выяснить, как оно себя может повести?
Re[12]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 13:22
Оценка:
Здравствуйте, N. I., Вы писали:

NI>prezident.mira:


NI>>>Нет, это особый случай вычисления *(T *)nullptr из серии "игнорируйте, что мы писали вон там, и руководствуйтесь описанием поведения именно для данного случая".


PM>>typeid не отменяет вычисления подвыражения вида *p и, если бы оно само по себе приводило к UB, то всё выражение с ним, как и остальное поведение программы стало бы undefined.


NI>typeid(*p) — это специальная конструкция, в отношении которой действуют особые правила, и поведение выражения *p определяется контекстом его использования.


"Специальная конструкция" в случае неполиморфных классов. И тогда да, контекст, в котором находится *p, будет unevaluated и будут действовать особые правила.
В случае полиморфных классов обычный вычисляемый контекст.

NI>к обособленному *p применяется lvalue-to-rvalue conversion, потому что это volatile-qualified lvalue, и даже введение понятия empty lvalue от undefined behavior тут уже не спасёт.


Я оценил глубину Ваших познаний.
Re[11]: const reference nullptr
От: N. I.  
Дата: 16.07.17 13:24
Оценка:
prezident.mira:

PM>Да. Выделили malloc-ом "storage with the proper alignment and size for type T" и, если тип "have non-vacuous initialization", вызвали конструктор с использованием placement new.


Требует объект нетривиальную инициализацию или нет, для его создания необходимо использование конструкции, которая может создавать такие объекты. Само по себе выделение памяти никаких объектов не создаёт, и malloc создавать объекты не умеет.

PM>Я уже не в первый раз замечаю, что Вы не особо чистоплотны в методах ведения дискуссии. Либо просто не умеете читать.

PM>Написано "malloc alone is not sufficient to create an object" — "только malloc-а недостаточно для создания [всякого] объекта".

Не всякого, а вообще хоть какого-либо.

PM>Да, malloc-а недостаточно для создания всякого объекта, т.к. для некоторых типов кроме выделения стораджа может быть необходимо вызвать нетривиальный конструктор. См. http://eel.is/c++draft/basic.life#1 опять же.


Там сказано "is not sufficient", а не "may be insufficient".

PM>Больше похоже на то, что участники забыли про существование http://eel.is/c++draft/basic.life#1


Ничего они не забыли. Я тоже когда-то ошибочно предполагал, что выделить память с помощью функций std::malloc или ::operator new достаточно, чтоб получить этакое неограниченное множество объектов, не требующих нетривиальную инициализацию, и дальше с ними работать. А потом я получил разъяснение от Майка Миллера (председателя CWG), что это так не работает и объекты создаются только специальными конструкциями, вроде определения переменной и оператора new. Хочешь создать объект, скажем, типа int в памяти, выделенной malloc, — изволь использовать placement new. Даже если никакая инициализация объекта не нужна, необходимо выполнить что-то вроде new(storage) int, а иначе у тебя будет только дырка от бублика, а не объект.
Re[11]: const reference nullptr
От: N. I.  
Дата: 16.07.17 13:27
Оценка:
prezident.mira:

PM>Ну вот Вам Ричард Смит https://groups.google.com/a/isocpp.org/d/msg/std-discussion/hG1Qx8a6Wjw/Q7aLfxfGAgAJ

....
PM>Ещё вопросы есть, или в ответ опять будет "И что?" и тонна бла-бла вида "ну это смотря как это понимать"?

Это чё, текст стандарта или выражение воли комитета по стандартизации? В подтверждение статуса де-факто я тебе сам сколько хочешь цитат приведу; покажи лучше, где стандарт де-юре описывает требования к поведению.
Отредактировано 16.07.2017 13:34 N. I. . Предыдущая версия .
Re[12]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 13:38
Оценка:
Здравствуйте, N. I., Вы писали:

NI>prezident.mira:


NI>>>И что? CWG решила, что разыменование нулевого указателя должно быть разрешено, но официального (закреплённого в стандарте) описания поведения программы при таком разыменовании (кроме специального случая с typeid) по-прежнему нет.


PM>>CWG решила, что дефекта в стандарте нет, поэтому менять в нём ничего не нужно.


NI>В каком конкретно месте его нет? Issue 232 уже закрыли?


232 — это исключительно про wording, а не про UB или не UB.

NI>Ещё раз: что будет результатом выражения *p, где p — нулевой указатель?


Что будет результатом — описано в пункте, который тут уже многократно цитировался и я не поленюсь сделать это ещё раз:

the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points


NI>"Не пойми что"?


"Не пойми что" там только из-за bad wording.

NI>И что я дальше могу делать с этим "не пойми что"?


Дальше Вы можете, например, применить оператор взятия адреса.

PM>>Написано: "shall be a pointer to an object type,". Т.е. говорится про тип выражения.

PM>>Я бы понял Ваши постоянные отсылки к определению UB, если бы было сказано "shall be a pointer to an object,".

NI>Мои отсылки делались именно к отсутствию явного определения поведения в таком случае.


Поведение определено во всех значениях, пока не сказано иное, как в процитированном Вами наже пункте про invalid pointer value.
И то, я подозреваю, что indirection throug означает использование его для доступа к значению, т.е. lvalue-to-rvalue conversion, а не просто применение оператора indirection.

PM>>Про значения указателя ничего не сказано — значит валидно для всех значений.


NI>Нет, не для всех. Применение indirection к invalid pointer value — это явное undefined behavior:


NI>[basic.stc.dynamic.deallocation]/4:

NI>

Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior.


Да. Поведение определено для всех значений, кроме тех, про которые явно не сказано, что оно не определено. Так же, как, например, со сложением int: не нужно перечислять все пары значений и их результат. Без этого понятно, что определено для всех. Кроме тех случаев, которые вызывают переполнение.

А пункт про "UB, если стандарт не определяет поведение" применяется примерно так:

Поведение описано для применения оператора * к выражению, которое имеет тип "указатель на объект (или функцию)". Что происходит при применении к выражению с другим типом? Это не определено, а, значит, по пункту про UB это UB.

А не так, как Вы его применяете:
NI>Null pointer value не является invalid pointer value, поэтому в случае с разыменованием null pointer неопределённое поведение — это только следствие отсутствия явно определённого поведения.


PM>>Требования к входному выражению предъявлены, результат описан.


NI>Результат, которого там на самом деле не может быть? И что я могу делать с этим результатом? И какие правила при этом должен применять? Например, я захотел использовать *(int volatile *)nullptr в качестве discarded-value-expression. В какие пункты стандарта мне смотреть, чтоб выяснить, как оно себя может повести?


На первый ряд вопросов я ответил выше, а про volatile Вы сами писали.
Re[12]: const reference nullptr
От: prezident.mira Россия  
Дата: 16.07.17 13:42
Оценка:
Здравствуйте, N. I., Вы писали:

NI>prezident.mira:


PM>>Ну вот Вам Ричард Смит https://groups.google.com/a/isocpp.org/d/msg/std-discussion/hG1Qx8a6Wjw/Q7aLfxfGAgAJ

NI>....
PM>>Ещё вопросы есть, или в ответ опять будет "И что?" и тонна бла-бла вида "ну это смотря как это понимать"?

NI>Это чё, текст стандарта или выражение воли комитета по стандартизации? В подтверждение статуса де-факто я тебе сам сколько хочешь цитат приведу; покажи лучше, где стандарт де-юре описывает требования к поведению.


Да, так я и предполагал. Сначала Вы требовали мнения авторитета из определённого круга лиц, а когда его предъявили — дали заднюю. В общем, слив засчитан.
Отредактировано 16.07.2017 13:44 prezident.mira . Предыдущая версия .
Re[13]: const reference nullptr
От: N. I.  
Дата: 16.07.17 16:32
Оценка:
prezident.mira:

PM>232 — это исключительно про wording, а не про UB или не UB.


Формулировки как раз и определяют поведение.

NI>>Ещё раз: что будет результатом выражения *p, где p — нулевой указатель?


PM>Что будет результатом — описано в пункте, который тут уже многократно цитировался и я не поленюсь сделать это ещё раз:

PM>

PM>the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points


Не будет там никакого lvalue referring to the object, просто в силу очевидной невозможности.

NI>>"Не пойми что"?


PM>"Не пойми что" там только из-за bad wording.


C составлением good wording, которое бы всех устраивало, дела пока как-то не очень, issue 232 висит уже заметно больше 10 лет. При этом стандартизаторам, наверное, уже 100500 раз задавали вопросы на этот счёт (и порядком ими задолбали), что, по идее, должно бы дать хороший стимул разрешить эту проблему. Однако признаков, что это наконец-то случится в ближайшем будущем, что-то пока не видать, а значит, не всё так просто.

NI>>И что я дальше могу делать с этим "не пойми что"?


PM>Дальше Вы можете, например, применить оператор взятия адреса.


И что я получу?

[expr.unary.op]/3:

Otherwise, if the type of the expression is T, the result has type “pointer to T” and is a prvalue that is the address of the designated object (1.7) or a pointer to the designated function.


Если lvalue не ссылается ни на объект, ни на функцию, то и адрес брать не у кого. Примерно такое же неявное undefined behavior, что и при разыменовании. Есть и другой похожий способ получения undefined behavior:

int &f()
{
    int local;
    return local;
}

int main()
{
    &f(); // undefined behavior
}

PM>И то, я подозреваю, что indirection throug означает использование его для доступа к значению, т.е. lvalue-to-rvalue conversion, а не просто применение оператора indirection.

Нет, это именно применение оператора разыменования, иначе там бы говорилось об accessing.

PM>Да. Поведение определено для всех значений, кроме тех, про которые явно не сказано, что оно не определено. Так же, как, например, со сложением int: не нужно перечислять все пары значений и их результат. Без этого понятно, что определено для всех. Кроме тех случаев, которые вызывают переполнение.


Хорошо, давай попробуем вычислить вот такое сложение: *(int *)nullptr + 1. Какое поведение тут будет?

PM>А пункт про "UB, если стандарт не определяет поведение" применяется примерно так:

PM>

PM>Поведение описано для применения оператора * к выражению, которое имеет тип "указатель на объект (или функцию)". Что происходит при применении к выражению с другим типом? Это не определено, а, значит, по пункту про UB это UB.

Согласно [intro.compliance], требование "the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type" относится к diagnosable semantic rules, и при его нарушении "a conforming implementation shall issue at least one diagnostic message" (это не undefined behavior).

PM>>>Требования к входному выражению предъявлены, результат описан.


По факту результат описан лишь для некоторых возможных случаев, но не для всех. Если описанный результат заведомо неверный, то ни о какой определённости речи быть не может.

NI>>Результат, которого там на самом деле не может быть? И что я могу делать с этим результатом? И какие правила при этом должен применять? Например, я захотел использовать *(int volatile *)nullptr в качестве discarded-value-expression. В какие пункты стандарта мне смотреть, чтоб выяснить, как оно себя может повести?


PM>На первый ряд вопросов я ответил выше, а про volatile Вы сами писали.


Вот только я не сказал, каким именно образом lvalue-to-rvalue conversion, будучи применённым к такому lvalue, порождает undefined behavior. И я бы с большим интересом посмотрел на выкладки, опирающиеся на явное указание того, что здесь имеет место undefined behavior (именно в соответствии с текстом стандарта, без правок, не имеющих официальный статус).
Отредактировано 16.07.2017 17:18 N. I. . Предыдущая версия .
Re[13]: const reference nullptr
От: N. I.  
Дата: 16.07.17 16:35
Оценка:
prezident.mira:

NI>>Это чё, текст стандарта или выражение воли комитета по стандартизации? В подтверждение статуса де-факто я тебе сам сколько хочешь цитат приведу; покажи лучше, где стандарт де-юре описывает требования к поведению.


PM>Да, так я и предполагал. Сначала Вы требовали мнения авторитета из определённого круга лиц


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

PM>В общем, слив засчитан.


В игры кто кому засчитает слив можешь идти играть с детишками на каком-нибудь ЛОРе. Мне до подобных "засчитываний" никакого дела нет, и, думаю, многим другим пользователям тоже, потому что сюда люди обычно приходят для того, чтобы делиться знаниями и узнавать что-то новое от других, что выгодно отличает данный ресурс от всяких флеймерских помоек.
Re[6]: const reference nullptr
От: σ  
Дата: 25.08.18 16:12
Оценка:
NI>Если описания поведения нет, то де-юре получаем не что иное, как undefined behavior:

Разве? У оператора разыменования не сказано, что otherwise behavior is undefined. Следовательно, это diagnosable (semantic) rule.
http://eel.is/c++draft/intro.compliance#def:diagnosable_rules : The set of diagnosable rules consists of all syntactic and semantic rules in this document except for those rules containing an explicit notation that “no diagnostic is required” or which are described as resulting in “undefined behavior”.

Ещё такое написано
http://eel.is/c++draft/intro.compliance#2.2: If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this document as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.

Получается, любая реализация, которая не выплёвывает diagnostic message при индеректе указателя, не указывающего на объект — не conforming?
Re[2]: const reference nullptr
От: B0FEE664  
Дата: 27.08.18 18:24
Оценка:
Здравствуйте, MasterZiv, Вы писали:

D>>
D>>const SomeClass& Class::getSomeClassVariable() const
D>>{
D>>    return *(SomeClass*)0;
D>>}
D>>

MZ>НАДО КИДАТЬ ИСКЛЮЧЕНИЕ!

Зачем исключение?
const SomeClass& Class::getSomeClassVariable() const
{
   assert(false && "It should never be called");
   static const SomeClass sSomeClass = SomeClass();
   return sSomeClass;
}
И каждый день — без права на ошибку...
Re: const reference nullptr
От: B0FEE664  
Дата: 04.09.18 08:19
Оценка:
Здравствуйте, Dair, Вы писали:

D>Есть ли смысл в подобном коде:

D>
D>const SomeClass& Class::getSomeClassVariable() const
D>{
D>    return *(SomeClass*)0;
D>}
D>

D>Или это яркий пример так называемого говнокода, как мне и показалось с первого взгляда?

Интересно, что в gcc std::array есть похожая конструкция:
 template<typename _Tp>
   struct __array_traits<_Tp, 0>
   {
     struct _Type { };

     static constexpr _Tp&   _S_ref(const _Type&, std::size_t) noexcept  { return *static_cast<_Tp*>(nullptr); }
   };


Используется в тех случаях, где по стандарту должно быть UB.
И каждый день — без права на ошибку...
Re[7]: const reference nullptr
От: N. I.  
Дата: 04.09.18 16:52
Оценка:
σ:

NI>>Если описания поведения нет, то де-юре получаем не что иное, как undefined behavior:


σ>Разве? У оператора разыменования не сказано, что otherwise behavior is undefined.


Там не только "otherwise", но и "if" поскипано.

σ>Получается, любая реализация, которая не выплёвывает diagnostic message при индеректе указателя, не указывающего на объект — не conforming?


А каким образом такое indirection что-то нарушает? Какие требования не выполняются?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.