web-dev-qa-db-fra.com

Avec C ++ 17, est-il possible de détecter si une structure / classe a une base?

J'ai besoin d'un trait de type qui sera vrai si le type donné dérive de quelque chose, et faux sinon.

Par exemple:

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
}

Est-il possible d'implémenter d'une manière ou d'une autre cette structure de trait "is_inherit"?


Pourquoi?

Je développe un constructeur de cadre de pile manuel pour Windows x64. Selon la documentation https://docs.Microsoft.com/en-us/cpp/build/return-values-cpp , si un type:

  • a une longueur de 1, 2, 4, 8, 16, 32 ou 64 bits;
  • n'a pas de constructeur, de destructeur ou d'opérateur d'affectation de copie défini par l'utilisateur;
  • n'a pas de membres de données non statiques privés ou protégés;
  • n'a pas de membres de données non statiques de type référence;
  • n'a pas de classes de base;
  • n'a pas de fonctions virtuelles;
  • et ne possède aucun membre de données qui ne répond pas également à ces exigences;

alors sa valeur de retour est dans le registre RAX, sinon la fonction a un argument caché que je dois détecter et gérer.

Auparavant, c'était la définition d'un POD C++ 03, mais en C++ 11, cela a changé:

Étant donné que la définition a changé dans la norme C++ 11, nous vous déconseillons d'utiliser std::is_pod pour ce test.

Jusqu'à présent, avec certains traits conjugués, je pouvais détecter si le type répondait ou non à la définition d'un POD C++ 03. Cependant, avec C++ 17, les règles d'agrégation ont changé, ce qui a brisé ma solution.

Si je peux en quelque sorte détecter si un type T a une classe de base, ma solution fonctionnera à nouveau.

37
Nyufu

Oui, c'est possible, du moins pour les agrégats.

Nous construisons d'abord un modèle de classe qui est convertible en n'importe quelle base appropriée de son paramètre de modèle:

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();
};

Ensuite, nous détectons si un paramètre de modèle T est constructible agrégé à partir d'une valeur de type 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 {};

Exemple .

32
ecatmur

Je crois que vérifier si "T dérive de quoi que ce soit" n'est pas possible, du moins pas de manière conforme aux normes. Si vous utilisez cette technique pour vérifier si un type est ou non un POD/trivial/agrégat, certains traits de type peuvent vous aider:

9
Vittorio Romeo