Какие из них являются неопределенным поведением:
template <class T> struct Struct { T t; };
template <class T> union Union { T t; };
template <class T> void function() {
Struct aS[10];
Union aU[10];
// do something with aS[9].t and aU[9].t including initialization
T *aSP = reinterpret_cast<T *>(aS);
T *aUP = reinterpret_cast<T *>(aU);
// so here is this undefined behaviour?
T valueS = aSP[9];
// use valueS in whatever way
// so here is this undefined behaviour?
T valueU = aUP[9];
// use valueU in whatever way
// now is accessing aS[9].t or aU[9].t now UB?
}
Так что да, какая из последних 3 операций является UB?
(Мои рассуждения: я не знаю о структуре, если есть какое-либо требование, чтобы ее размер был таким же, как и ее единственный элемент, но, насколько я знаю, объединение должно быть того же размера, что и элемент. Требования к выравниванию Я не знаю для союза, но я предполагаю, что это одно и то же. Для структуры я понятия не имею. В случае союза я бы предположил, что это не UB, но, как я уже сказал, я действительно действительно не уверен. структура, которую я на самом деле понятия не имею)
Вместо того, чтобы заставлять нас делать домашнее задание за вас, расскажите нам, что думает ты и почему, и попросите нас исправить или подтвердить некоторые конкретные рассуждения.
См. также stackoverflow.com/a/25377970/560648 и en.cppreference.com/w/cpp/types/is_standard_layout. А что такое T? Вы не создавали никаких T, но в зависимости от того, что такое T, это может не иметь значения... но вы должны предоставить всю необходимую информацию и контекст.
Как указано, обе последние 2 операции являются UB.





Я нашел c++ - Является ли sizeof (T) == sizeof (int). Это указывает, что структуры не обязательно должны иметь тот же размер, что и их элементы (вздох). Что касается союзов, то, вероятно, применимо то же самое (после прочтения ответов я пришел к выводу, что так). Это само по себе необходимо, чтобы сделать эту ситуацию UB. Однако, если sizeof(Struct) == sizeof(T) и «Установлено, что» в https://stackoverflow.com/a/21515546, указатель на aSP[9] будет в том же месте, что и у aS[9] (по крайней мере, я так думаю), и переинтерпретация, которая гарантируется по стандарту (согласно цитате в https://stackoverflow.com/a/21509729).
Обновлено: Это на самом деле неправильно. Правильный ответ: здесь.
Итак, если мы посмотрим на документ reinterpret_cast (здесь)
5) Any object pointer type T1* can be converted to another object pointer type cv T2*. This is exactly equivalent to static_cast(static_cast(expression)) (which implies that if T2's alignment requirement is not stricter than T1's, the value of the pointer does not change and conversion of the resulting pointer back to its original type yields the original value). In any case, the resulting pointer may only be dereferenced safely if allowed by the type aliasing rules (see below)
Теперь что говорят правила псевдонимов?
Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:
- AliasedType and DynamicType are similar.
- AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.
- AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.
Так что это не 2 и не 3. Может быть 1?
Аналогичный:
Informally, two types are similar if, ignoring top-level cv-qualification:
- they are the same type; or
- they are both pointers, and the pointed-to types are similar; or
- they are both pointers to member of the same class, and the types of the pointed-to members are similar; or
- they are both arrays of the same size or both arrays of unknown bound, and the array element types are similar.
Two objects a and b are pointer-interconvertible if:
- they are the same object, or
- one is a union object and the other is a non-static data member of that object ([class.union]), or
- one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, any base class subobject of that object ([class.mem]), or
- there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast. [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note]
Итак, для меня:
T *aSP = reinterpret_cast<T *>(aS); // Is OK
T *aUP = reinterpret_cast<T *>(aU); // Is OK.
Вкратце: последние два оператора в приведенном выше коде всегда будут вызывать неопределенное поведение, простое приведение указателя на объединение к указателю на один из его типов членов, как правило, нормально, потому что на самом деле оно ничего не делает (в худшем случае оно не указано). , но никогда не неопределенное поведение; примечание: мы говорим только о самом приведении, использование результата приведения для доступа к объекту — это совсем другая история).
В зависимости от того, чем в конечном итоге будет T, Struct<T> потенциально может быть структурой стандартного макета [класс.проп]/3, и в этом случае
T *aSP = reinterpret_cast<T *>(aS);
будет хорошо определен, потому что Struct<T> будет взаимопреобразовываться указателем со своим первым членом (который имеет тип T) [базовый.состав]/4.3. Выше reinterpret_cast эквивалентно [expr.reinterpret.cast]/7
T *aSP = static_cast<T *>(static_cast<void *>(aS));
который вызовет преобразование массива в указатель [конв.массив], в результате чего Struct<T>* укажет на первый элемент aS. Затем этот указатель преобразуется в void* (через [expr.static.cast]/4 и [конв.ptr]/2), который затем преобразуется в T*, что допустимо через [expr.static.cast]/13:
A prvalue of type “pointer to cv1
void” can be converted to a prvalue of type “pointer to cv2T”, whereTis an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If the original pointer value represents the addressAof a byte in memory andAdoes not satisfy the alignment requirement ofT, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an objecta, and there is an objectbof typeT(ignoring cv-qualification) that is pointer-interconvertible witha, the result is a pointer tob. Otherwise, the pointer value is unchanged by the conversion.
По аналогии,
T *aUP = reinterpret_cast<T *>(aU);
будет четко определен в C++ 17, если Union<T> является объединением стандартной компоновки и в целом выглядит четко определенным в будущей версии C++, основанной на текущем стандартном черновике, где объединение и один из его членов всегда взаимопреобразуемый указатель [базовый.состав]/4.2
Однако все вышеперечисленное не имеет значения, поскольку
T valueS = aSP[9];
а также
T valueU = aUP[9];
будет вызывать неопределенное поведение, несмотря ни на что. aSP[9] и aUP[9] (по определению) такие же, как *(aSP + 9) и *(aUP + 9) соответственно [expr.sub]/1. Арифметика указателя в этих выражениях подчиняется [расшир.доб.]/4.
When an expression
Jthat has integral type is added to or subtracted from an expressionPof pointer type, the result has the type ofP.
- If
Pevaluates to a null pointer value andJevaluates to 0, the result is a null pointer value.- Otherwise, if
Ppoints to elementx[i]of an array objectxwith n elements, the expressionsP + JandJ + P(whereJhas the value j) point to the (possibly-hypothetical) elementx[i+j]if 0≤i+j≤n and the expressionP - Jpoints to the (possibly-hypothetical) elementx[i−j]if 0≤i−j≤n.- Otherwise, the behavior is undefined.
aSP и aUP не указывают на элемент массива. Даже если aSP и aUP будут взаимопреобразовывать указатели с T, вам будет разрешен доступ только к элементу 0 и вычисление адреса (но не доступа) к элементу 1 гипотетического одноэлементного массива…
Я не понимаю [basic.compound]/4.2, как ты. Для меня здесь у нас есть указатель на объединение и указатель на тот же тип, что и элемент объединения. Не «один является объектом объединения, а другой является нестатическим элементом данных этого объединения». Хотя может ошибся
@MartinMorterol Я не уверен, что ты имеешь в виду. Объединение (стандартного макета) и один из его элементов, взаимопреобразующий указатель, просто означает (через [expr.static.cast]/13, как указано выше), что результат приведения указателя к единице к типу «указатель на тип другого" будет допустимым значением указателя, указывающим на другой объект...
Хорошо, спасибо, я понял. Но я не понимаю, почему вы говорите: «aUP не указывает на элемент массива». Из [conv.array] «Результатом является указатель на первый элемент массива». Итак, aUP должен указывать на элемент массива?
@MartinMorterol результат преобразования массива в указатель на aS и aU указывает на объект, который является первым элементом массива. Но результат reinterpret_cast будет (в лучшем случае) указывать на объект, который является подобъектом (членом) первого объекта, и этот подобъект не является элементом массива…
Так правда ли, что T valueS = aSP[0] — это неопределенное поведение?
@ Леонид да, и T valueU = aUP[9]; тоже.
Что ты считает UB, почему и почему вы не уверены?