Con C++17 es posible detectar si una estructura/clase tiene alguna base?


Necesito un rasgo de tipo que será verdadero si el tipo dado deriva de cualquier cosa, y falso de lo contrario.

Por ejemplo:

template<class T>
struct is_inherit
    //... logic of inheritance detection
    ;

template<class T>
void AppLogic(){
    if constexpr(is_inherit<T>::value) {
        puts("T has base");
        //...
    } else {
        puts("T doesn't have base");
        //...
    }
}

struct A {};
struct C {};
struct B: C {};

int main() {
    AppLogic<A>(); // print: T doesn't have base 
    AppLogic<B>(); // print: T has base
}

¿Es posible implementar de alguna manera esa estructura de rasgo "is_inherit"?


¿Por qué?

Estoy desarrollando un generador de marcos de pila manual para Windows x64. Según el https://docs.microsoft.com/en-us/cpp/build/return-values-cpp documentación, si es un tipo:

  • tiene una longitud de 1, 2, 4, 8, 16, 32, o 64 bits;
  • no tiene constructor, destructor u operador de asignación de copia definido por el usuario;
  • no tiene miembros de datos no estáticos privados o protegidos;
  • no tiene miembros de datos no estáticos de tipo de referencia;
  • no tiene clases base;
  • no tiene funciones virtuales;
  • y no tiene miembros de datos que tampoco cumplan con estos requisitos;

Entonces su valor de retorno está en el registro RAX, de lo contrario la función tiene un argumento oculto que debo detectar y manejar.

Esta solía ser la definición de un POD de C++03, sin embargo en C++11 esto cambió:

Debido a que la definición ha cambiado en el estándar C++11, no recomendamos usar std::is_pod para esta prueba.

Hasta ahora, con algunos rasgos conjugados podía detectar si el tipo cumplía con la definición de un POD C++03 o no. Sin embargo, con C++17 las reglas agregadas han cambiado, y eso rompió mi solución.

Si de alguna manera puedo detectar si un tipo T tiene alguna clase base, mi solución funcionará de nuevo.

Author: Boann, 2017-09-13

2 answers

Sí, esto es posible, al menos para los agregados.

Primero construimos una plantilla de clase que es convertible a cualquier base apropiada de su parámetro de plantilla:

template<class T>
struct any_base {
    operator T() = delete;
    template<class U, class = std::enable_if_t<std::is_base_of_v<U, T>>> operator U();
};

Luego detectamos si un parámetro de plantilla T es construible a partir de un valor de tipo any_base<T>:

template<class, class = void> struct has_any_base : std::false_type {};
template<class T>
struct has_any_base<T, std::void_t<decltype(T{any_base<T>{}})>> : std::true_type {};

Ejemplo.

 32
Author: ecatmur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-13 13:56:19

Creo que comprobar si "T deriva de algo" no es posible, al menos no de una manera compatible con el estándar. Si está utilizando esta técnica para verificar si un tipo es un POD/trivial/aggregate, hay algunos rasgos de tipo que podrían ayudarlo:

 9
Author: Vittorio Romeo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-13 11:29:53