`std:: complex <T> [n]` и `T [n * 2]` тип сглаживания

Так как С++ 11 std::complex<T>[n] гарантированно является алиасифицированным как T[n*2], с четко определенными значениями. Это именно то, чего можно было бы ожидать для любой основной архитектуры. Является ли эта гарантия достижимой со стандартным С++ для моих собственных типов, скажем struct vec3 { float x, y, z; } или это возможно только при специальной поддержке компилятора?

Ответ 1

TL; DR. Компилятор должен проверить reinterpret_cast и выяснить, что задействованы (стандартная библиотека) специализации std::complex. Мы не можем соответствующим образом имитировать семантику.

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

Итак, пусть vec3 содержит массив из трех int. Даже тогда базовый reinterpret_cast<int*>(&v), который вам неявно нужен (где v является vec3), не оставляет вас указателем на первый элемент. См. Исчерпывающие требования к указатель-взаимопревращение:

Два объекта a и b являются взаимно обратимыми для указателей, если:

  • они являются одним и тем же объектом, или

  • один представляет собой объект объединения стандартного макета, а другой - нестатический член данных этого объекта ([class.union]) или

  • один является объектом класса стандартного макета, а другой является первым нестатическим членом данных этого объекта или, если объект не имеет нестатические элементы данных, первый подобъект базового класса этого объекта ([class.mem]) или

  • существует объект c такой, что a и c являются взаимно обратимыми, а c и b являются указатель равноценно.

Если два объекта взаимно конвертируемы, то они имеют одинаковые адрес, и можно получить указатель на один из указателя к другому через a reinterpret_­cast. [Примечание: Объект массива и его первый элемент не является взаимно конвертируемым, хотя они имеют тот же адрес. - конец примечания]

Это совершенно однозначно; в то время как мы можем получить указатель на массив (являющийся первым членом), а в то время как взаимная конвертация указателя транзитивна, мы не можем получить указатель на свой первый элемент.

И, наконец, даже если вам удалось получить указатель на первый элемент вашего массива-члена, если у вас был массив vec3 s, вы не сможете пройти все массивы-члены с помощью простых указателей указателя, поскольку мы получаем указатели прошлый конец массивов между ними. также не решает эту проблему, поскольку объекты, с которыми связаны указатели, не имеют общего хранилища (cf [ptr.launder] для специфики).

Ответ 2

Это возможно только при особой поддержке компилятора.

Союзы не получают вас там, потому что общий подход имеет поведение undefined, хотя есть исключения для исходных последовательностей, совместимых с макетами, и вы можете проверить объект через unsigned char* как особый случай. Что это, тем не менее.

Интересно, что, если мы не примем широкое и бесполезное значение "ниже", стандарт технически противоречив в этом отношении:

[C++14: 5.2.10/1]: [..] Ниже перечислены преобразования, которые могут быть выполнены явно с использованием reinterpret_cast. Никакое другое преобразование не может быть выполнено явно с использованием reinterpret_cast.

Случай для complex<T> не упоминается. Наконец, правило, о котором вы говорите, вводится гораздо позже, в [C++14: 26.4/4].

Ответ 3

Я думаю, что это сработало бы для одного vec3, если бы ваш тип содержал float x[3], а вы обеспечили sizeof(vec3) == 3*sizeof(float) && is_standard_layout_v<vec3>. Учитывая эти условия, стандарт гарантирует, что первый член имеет нулевой сдвиг, поэтому адрес первого float является адресом объекта, и вы можете выполнить арифметику массива, чтобы получить другие элементы в массиве:

struct vec3 { float x[3]; } v = { };
float* x = reinterpret_cast<float*>(&v);  // points to first float
assert(x == v.x);
assert(&x[0] == &v.x[0]);
assert(&x[1] == &v.x[1]);
assert(&x[2] == &v.x[2]);

То, что вы не можете сделать, это обработать массив vec3 как массив поплавков в три раза больше длины. Арифметика массива в массиве внутри каждого vec3 не позволит вам получить доступ к массиву в следующем vec3. CWG 2182 здесь.