Armazenamento baseado em char genérico [] e evitando o UB relacionado ao estrito aliasing

Eu estou tentando construir um modelo de classe que empacota um monte de tipos em uma matriz de caracteres adequadamente grande e permite acesso aos dados como referências de digitação corretamente individuais. Agora, de acordo com o padrão, isso pode levar a uma violação estritamente alias e, portanto, ao comportamento indefinido, já que estamos acessandochar[] dados através de um objeto que não é compatível com ele. Especificamente, o padrão afirma:

Se um programa tentar acessar o valor armazenado de um objeto por meio de um valor diferente de um dos seguintes tipos, o comportamento é indefinido:

o tipo dinâmico do objeto,uma versão qualificada em cv do tipo dinâmico do objeto,um tipo similar (como definido em 4.4) ao tipo dinâmico do objeto,um tipo que é o tipo assinado ou não assinado correspondente ao tipo dinâmico do objeto,um tipo que é o tipo assinado ou não assinado correspondente a uma versão qualificada em cv do tipo dinâmico do objeto,um tipo de agregado ou união que inclua um dos tipos mencionados acima entre seus elementos ou membros de dados não estáticos (incluindo, recursivamente, um elemento ou membro de dados não estáticos de uma união subagregada ou contida),um tipo que é um tipo de classe base (possivelmente qualificado por cv) do tipo dinâmico do objeto,a char ouunsigned char tipo.

Dada a redação do ponto destacado, eu apresentei o seguintealias_cast idéia:

#include <iostream>
#include <type_traits>

template <typename T>
T alias_cast(void *p) {
    typedef typename std::remove_reference<T>::type BaseType;
    union UT {
        BaseType t;
    };
    return reinterpret_cast<UT*>(p)->t;
}

template <typename T, typename U>
class Data {
    union {
        long align_;
        char data_[sizeof(T) + sizeof(U)];
    };
public:
    Data(T t = T(), U u = U()) { first() = t; second() = u; }
    T& first() { return alias_cast<T&>(data_); }
    U& second() { return alias_cast<U&>(data_ + sizeof(T)); }
};


int main() {
    Data<int, unsigned short> test;
    test.first() = 0xdead;
    test.second() = 0xbeef;
    std::cout << test.first() << ", " << test.second() << "\n";
    return 0;
}

(O código de teste acima, especialmente oData A aula é apenas uma demonstração estúpida da idéia, então, por favor, não aponte como eu devo usarstd::pair oustd::tuple. oalias_cast template também deve ser estendido para manipular tipos qualificados cv e só pode ser usado com segurança se os requisitos de alinhamento forem atendidos, mas espero que este trecho seja suficiente para demonstrar a idéia.)

Este truque silencia os avisos por g + + (quando compilado comg++ -std=c++11 -Wall -Wextra -O2 -fstrict-aliasing -Wstrict-aliasing), e o código funciona, mas esta é realmente uma maneira válida de dizer ao compilador para pular as otimizações baseadas em aliases restritas?

Se não é válido, então como alguém poderia implementar uma classe de armazenamento genérica baseada em matriz de caracteres como essa sem violar as regras de alias?

Editar: substituindo oalias_cast com um simplesreinterpret_cast como isso:

T& first() { return reinterpret_cast<T&>(*(data_ + 0)); }
U& second() { return reinterpret_cast<U&>(*(data_ + sizeof(T))); }

produz o seguinte aviso quando compilado com g ++:

aliastest-so-1.cpp: Na instanciação de 'T & Data :: first () [com T = int; U = short unsigned int] ’: aliastest-so-1.cpp: 28: 16:
necessário a partir daqui aliastest-so-1.cpp: 21: 58: aviso: o ponteiro do tipo puncionado do dereferencing quebrará as regras de serrilhamento estrito [-Withrict-aliasing]

questionAnswers(1)

yourAnswerToTheQuestion