¿Qué tipo de rasgo indicaría que tipo es memcpy asignable? (tupla, par)

Me gustaría saber qué tipo de introspección puedo hacer para detectar tipos que se pueden asignar simplemente copiando la memoria sin formato.

Por ejemplo, hasta donde entiendo, los tipos incorporados tuplas de tipos incorporados y tuplas de tales tuplas, caerían en esta categoría. La motivación es que quiero transportar bytes sin procesar si es posible.

T t1(...); // not necessarely default constructible 
T t2(...);

t1 = t2; // should be equivalent to std::memcpy(&t1, &t2, sizeof(T));
// t1 is now an (independent) copy of the value of t2, for example each can go out of scope independently

Quétype_trait ocombinación detype_traits podría decir en el tipo de compilación si la asignación puede ser (en principio) reemplazada pormemcpy?

Intenté lo que funcionaría para los tipos que supongo que deberían cumplir con esta condición y, para mi sorpresa, el único que se ajusta al comportamiento no esstd::is_trivially_assignable perostd::trivially_destructible. Tiene sentido hasta cierto punto, pero estoy confundido por qué algunas otras opciones no funcionan con los casos esperados.

Entiendo que puede que no haya un método a prueba de balas porque siempre se puede escribir una clase que efectivamente sea memcopiable, que no se pueda "detectar" como memcopilable, pero estoy buscando uno que funcione para los casos intuitivos simples.

#include<type_traits>
template<class T> using trait = 
    std::is_trivially_destructible
//  std::is_trivial
//  std::is_trivially_copy_assignable
//  std::is_trivially_copyable // // std::tuple<double, double> is not trivially copyable!!!
//  std::is_trivially_default_constructible
//  std::is_trivially_default_constructible
//  std::is_trivially_constructible
//  std::is_pod // std::tuple<double, double> is not pod!!!
//  std::is_standard_layout
//  std::is_aggregate
//  std::has_unique_object_representations
    <T>
;

int main(){
    static_assert((trait<double>{}), "");
    static_assert((trait<std::tuple<double, double>>{}), "");
    static_assert((not trait<std::tuple<double, std::vector<double>>>{}), "");
    static_assert((not trait<std::vector<double>>{}), "");
}

Por supuesto, mi convicción de que la tupla debe ser membresía no se basa en el estándar sino en el sentido común y la práctica. Es decir, porque esto generalmente está bien:

std::tuple<double, std::tuple<char, int> > t1 = {5.1, {'c', 8}};
std::tuple<double, std::tuple<char, int> > t2;
t2 = t1;
std::tuple<double, std::tuple<char, int> > t3;
std::memcpy(&t3, &t1, sizeof(t1));
assert(t3 == t2);

Como prueba de principio, implementé esto.Agregué un par de condiciones relacionadas con el tamaño para evitar alguna posible especialización engañosa destd::tuple.

template<class T> 
struct is_memcopyable 
: std::integral_constant<bool, std::is_trivially_copyable<T>{}>{};

template<class T, class... Ts> 
struct is_memcopyable<std::tuple<T, Ts...>> : 
    std::integral_constant<bool, 
        is_memcopyable<T>{} and is_memcopyable<std::tuple<Ts...>>{}
    >
{};

template<class T1, class T2> 
struct is_memcopyable<std::pair<T1, T2>> : 
    std::integral_constant<bool, 
        is_memcopyable<T1>{} and is_memcopyable<T2>{}
    >
{};

Esta es una solución muy limitada porque una clase como:

struct A{ std::tuple<double, double> t; };

desafortunadamente, se informará que no se puede copiar trivialmente y que no se puede copiar.

Respuestas a la pregunta(3)

Su respuesta a la pregunta