Информация об изменениях

Сообщение Как обойти strict aliasing rule? от 16.06.2016 22:40

Изменено 21.06.2016 9:55 S. Schlongberg

У меня есть некая структурка данных, и к ней надо обращаться то как к двум 32-битным беззнаковым интам, то как к одному 64-битному беззнаковому инту.

По сути, это union. Но, насколько я понимаю, через union по стандарту C++ этого делать нельзя, так как это нарушение strict aliasing и UB (записывать один тип, а читать другой, несовместимый, тип).

struct Foo
{
    union
    {
        std::uint64_t value64;

        struct 
        {
            std::uint32_t value32Lo;

            std::uint32_t value32Hi;           
        };
    };
};

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

Еще у меня была мысль про буфер char'ов: по стандарту любой тип можно алиазить массивом чаров, тогда что если записать в этот массив std::uint64_t, а прочитать два std::uint32_t — будет ли это нарушением strict aliasing rule? Очевидный ответ — да, поскольку читается один тип, а записывается другой. А может и нет. Этот момент мне неясен.

struct Foo
{
    alignas(std::uint64_t) char buffer[sizeof(std::uint64_t)];
};


Big endian я не буду поддерживать, как как для этого нужно во всем коде специально делать поддержку и тщательно его весь именно для этого тестировать, что совершенно не стоит потраченных усилий. Так же, как я не собираюсь поддерживать системы меньше 64 бит.

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