Generación basada en char [] genérica y evitando la UB relacionada con el estricto aliasing

Estoy tratando de crear una plantilla de clase que incluya un montón de tipos en una matriz de caracteres adecuadamente grande, y permita el acceso a los datos como referencias individuales correctamente escritas. Ahora, de acuerdo con el estándar, esto puede llevar a una violación de alias estricta, y por lo tanto a un comportamiento indefinido, ya que estamos accediendo a lachar[] datos a través de un objeto que no es compatible con él. Específicamente, la norma establece:

Si un programa intenta acceder al valor almacenado de un objeto a través de un glvalue distinto de uno de los siguientes tipos, el comportamiento no está definido:

el tipo dinámico del objeto,una versión cv calificada del tipo dinámico del objeto,un tipo similar (como se define en 4.4) al tipo dinámico del objeto,un tipo que es el tipo con signo o sin signo correspondiente al tipo dinámico del objeto,un tipo que es el tipo firmado o sin firmar que corresponde a una versión calificada de cv del tipo dinámico del objeto,un tipo agregado o de unión que incluye uno de los tipos mencionados anteriormente entre sus elementos o miembros de datos no estáticos (incluido, recursivamente, un elemento o miembro de datos no estáticos de un subagregado o unión contenida),un tipo que es un tipo de clase base (posiblemente cv calificado) del tipo dinámico del objeto,a char ounsigned char tipo.

Dada la redacción de la viñeta resaltada, se me ocurrió lo siguiente:alias_cast idea:

#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;
}

(El código de prueba anterior, especialmente elData La clase es solo una demostración de la idea, así que, por favor, no me indiques cómo debo usarla.std::pair ostd::tuple. losalias_cast la plantilla también debe extenderse para manejar tipos calificados de cv y solo se puede usar de forma segura si se cumplen los requisitos de alineación, pero espero que este fragmento sea suficiente para demostrar la idea.)

Este truco silencia las advertencias de g ++ (cuando se compila cong++ -std=c++11 -Wall -Wextra -O2 -fstrict-aliasing -Wstrict-aliasing), y el código funciona, pero ¿es esto realmente una forma válida de decirle al compilador que omita las optimizaciones basadas en alias estrictos?

Si no es válido, entonces, ¿cómo se podría implementar una clase de almacenamiento genérico basado en una matriz de caracteres como este sin violar las reglas de aliasing?

Editar: reemplazando elalias_cast con un simplereinterpret_cast Me gusta esto:

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

produce la siguiente advertencia cuando se compila con g ++:

aliastest-so-1.cpp: En la instanciación de ‘T & Data :: first () [con T = int; U = int sin signo corto] ’: aliastest-so-1.cpp: 28: 16:
se requiere desde aquí aliastest-so-1.cpp: 21: 58: advertencia: la desreferenciación del puntero con tipo de letra infringirá las reglas de alias estricto [-Wstrict-aliasing]

Respuestas a la pregunta(1)

Su respuesta a la pregunta