Ниже приведен упрощенный пример помеченного шаблона объединения «Хранение», в котором могут быть два типа L и R, заключенные в объединение, плюс логическое значение, указывающее, какой из них хранится. В экземпляре используются два типа разных размеров, причем на самом деле меньший является пустым.
#include <utility>
struct Empty
{
};
struct Big
{
long a;
long b;
long c;
};
template<typename L, typename R>
class Storage final
{
public:
constexpr explicit Storage(const R& right) : payload{right}, isLeft{false}
{
}
private:
union Payload
{
constexpr Payload(const R& right) : right{right}
{
}
L left;
R right;
};
Payload payload;
bool isLeft;
};
// Toggle constexpr here
constexpr static Storage<Big, Empty> createStorage()
{
return Storage<Big, Empty>{Empty{}};
}
Storage<Big, Empty> createStorage2()
{
return createStorage();
}
- Конструктор инициализирует R-член с помощью Empty и вызывает только конструктор объединения для этого члена
- Объединение никогда не инициализируется по умолчанию как единое целое.
- Все конструкторы являются constexpr
Функция "createStorage2" должна для этого только заполнять тег bool и оставлять объединение в покое. Поэтому я ожидаю, что результат компиляции с оптимизацией по умолчанию "-O":
createStorage2():
mov rax, rdi
mov BYTE PTR [rdi+24], 0
ret
И G CC, и I CC вместо этого генерируют что-то вроде
createStorage2():
mov rax, rdi
mov QWORD PTR [rdi], 0
mov QWORD PTR [rdi+8], 0
mov QWORD PTR [rdi+16], 0
mov QWORD PTR [rdi+24], 0
ret
, обнуляя все 32-байтовая структура, в то время как Clang генерирует ожидаемый код. Вы можете воспроизвести это с https://godbolt.org/z/VsDQUu. G CC вернется к желаемой инициализации тега bool, только когда вы удалите constexpr из функции "createStorage" stati c, в то время как I CC остается не впечатленным и по-прежнему заполняет все 32 байта.
Это, вероятно, не является стандартным нарушением, поскольку неиспользуемые биты «неопределены», что угодно, в том числе установка на ноль и использование ненужных циклов ЦП. Но это раздражает, если вы сначала создали профсоюз по соображениям эффективности, и ваши члены профсоюза сильно различаются по размеру.
Что здесь происходит? Есть ли способ обойти это поведение при условии, что удаление constexpr из конструкторов и функции stati c не является опцией?
Примечание: I CC, кажется, выполняет некоторые дополнительные операции, даже когда все constexpr удалены, как в https://godbolt.org/z/FnjoPC:
createStorage2():
mov rax, rdi #44.16
mov BYTE PTR [-16+rsp], 0 #39.9
movups xmm0, XMMWORD PTR [-40+rsp] #44.16
movups xmm1, XMMWORD PTR [-24+rsp] #44.16
movups XMMWORD PTR [rdi], xmm0 #44.16
movups XMMWORD PTR [16+rdi], xmm1 #44.16
ret #44.16
Какова цель этих инструкций по перемещениям?