2017-09-13 55 views
35

Potrzebuję cechy typu, która będzie prawdziwa, jeśli dany typ pochodzi od czegokolwiek, a fałsz w przeciwnym razie.W C++ 17 można wykryć, czy struktura/klasa ma jakąkolwiek podstawę?

Na przykład:

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 
} 

Czy to możliwe, aby w jakiś sposób wykonania tego "is_inherit" cecha struct?


Dlaczego?

Pracuję nad ręcznym budowaniem ramek stosu dla systemu Windows x64. Zgodnie z dokumentacją https://docs.microsoft.com/en-us/cpp/build/return-values-cpp, jeśli typ:

  • ma długość 1, 2, 4, 8, 16, 32 lub 64 bitów;
  • nie ma zdefiniowanego przez użytkownika konstruktora, destruktora lub operatora przypisania kopii;
  • nie ma prywatnych ani chronionych niestatycznych elementów danych;
  • nie ma niestatycznych elementów danych typu referencyjnego;
  • nie ma klas podstawowych;
  • nie ma funkcji wirtualnych;
  • i nie ma elementów danych, które nie spełniają również tych wymagań;

wtedy jego wartość zwracana jest w rejestrze RAX, w przeciwnym razie funkcja ma ukryty argument, który muszę wykryć i obsłużyć.

To było definicja C++ 03 POD jednak w C++ 11 zmieniło:

Ponieważ definicja uległa zmianie w standardzie C++ 11, nie zaleca przy użyciu testu std::is_pod.

Do tej pory, z pewnymi skoniugowanymi cechami, mogłem wykryć, czy typ spełnia definicję C++ 03 POD, czy nie. Jednak w C++ 17 zmieniły się zasady agregacji, które zepsuły moje rozwiązanie.

Jeśli uda mi się jakoś wykryć, czy typ T ma jakąkolwiek klasę podstawową, moje rozwiązanie będzie działać ponownie.

+0

Jeśli ma podstawową * ogólnie *? Nie myśl, że możesz to zrobić bez "oszustwa". – StoryTeller

+1

musisz poczekać na dodanie introspekcji do C++ lub znaleźć sztuczkę kompilatora. Nie sądzę, że możesz to zrobić inaczej. Ale, jak zasugerował Vittorio Romeo, może to być problem XY. Dlaczego tego potrzebujesz? Może istnieć lepszy sposób na rozwiązanie pierwotnego problemu. – bolov

+0

Tak, ogólnie rzecz biorąc, wykryj, że dany typ ma jakąkolwiek podstawę, nie podoba mi się std :: is_base_of gdzie muszę podać 2 typy, a zwraca cecha, X dziedziczy Y. – Nyufu

Odpowiedz

31

Tak, jest to możliwe, przynajmniej dla agregatów.

Najpierw musimy skonstruować klasy szablon, który jest zamienny do każdej właściwej podstawy jej parametru szablonu:

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

Potem wykryć, czy parametr szablonu T jest agregat constructible od wartości typu 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 {}; 

Example.

+4

, jeśli Miałem klasę jak "klasa E {E (B &) {}};" Obawiam się, że has_any_base_v powiedziałby prawdę – gu1d0

+1

@ gu1d0 o tak, zapomniałem, że 'std :: is_base_of' mówi, że klasa jest własną bazą. zezwalaj tylko na prawidłowe bazy. Dzięki! – ecatmur

+0

@ gu1d0 yep, ale jeśli E ma żadnego konstruktora, to nie jest trywialne, dlatego nie jest agregatem. ecatmur napisał, że to rozwiązanie działa "przynajmniej dla agregatów" .To będzie częścią mojego C++ 03 Detekcja wykrywania POD, dlatego to rozwiązanie jest dla mnie wystarczające 'template struct is_cpp03_pod: std :: bool_constant && std :: is_aggregate_v &&! Has_any_base_v > {}; ' – Nyufu

8

Uważam, że sprawdzenie, czy "T pochodzi od czegokolwiek" nie jest możliwe, przynajmniej nie w sposób zgodny z normami. Jeśli używasz tej techniki w celu sprawdzenia, czy dany typ jest POD/trywialne/kruszywo, istnieją pewne cechy typu, które mogą pomóc:

+0

std :: is_base_of –

+1

@ Dúthomhas: przeczytaj pytanie –