Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist
-
Hallo zusammen
ich möchte ermitteln ob eine Instanz von B in Wirklichkeit vom Typ T ist ( wobei T abgeleitet von B ) ist, OHNE dynamic_cast o.ä. zu nutzen.
Gibt es da aus der Meta-Programmierung was nützliches? std::is_base_of wird da ja nichts bringen.gruß Tobi
-
@It0101 Warum nicht? Also okay, muss halt C++11 sein, aber ansonsten ist das kein Problem.
#include <iostream> #include <type_traits> class Base1 {}; class Base2 {}; class Derived1 : public Base1 {}; class Derived2 : public Base2 {}; int32_t main() { Base1 b1; Base2 b2; Derived1 d1; Derived2 d2; std::cout << "Base1 ? Derived1: " << std::is_base_of<Base1,decltype(d1)>::value << "\n" << "Base1 ? Derived2: " << std::is_base_of<Base1,decltype(d2)>::value << "\n" << "Base1 ? Derived1: " << std::is_base_of<decltype(b1),decltype(d1)>::value << "\n" << "Base2 ? Derived1: " << std::is_base_of<decltype(b2),decltype(d1)>::value << std::endl; return 0; }
Base1 ? Derived1: 1 Base1 ? Derived2: 0 Base1 ? Derived1: 1 Base2 ? Derived1: 0
-
@VLSI_Akiko sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
@It0101 Warum nicht? Also okay, muss halt C++11 sein, aber ansonsten ist das kein Problem.
Dein Code funktioniert doch aber nicht für das Problem von @It0101, weil das
is_base_of
das nur zur Compilezeit kann. Wenn ich @It0101 richtig verstehe, ist doch genaudynamic_cast
die Lösung (warum willst du das nicht?).Versuche dein Beispiel mal mit
Base1 *ptr_d1 = new Derived1();
Denn "eine Instanz von B, die in Wirklichkeit vom Typ BDerived ist", hätte ohne den Pointer ja Slicing zur Folge, das meint @It0101 bestimmt nicht.
MAn könnte ansonsten noch mit
typeid
testen:struct Base { virtual ~Base() = default; }; //wichtig: muss polymorphisch sein! struct Derived : public Base {}; Base *bptr = new Derived(); typeid(*bptr) == typeid(Base) ==> (bool) false typeid(*bptr) == typeid(Derived) ==> (bool) true
-
Und die letzte offensichtliche Variante wäre dann noch eine virtuelle Funktion in der Basisklasse einzubauen.
Es wäre aber die Frage interessant: wieso willst du das überhaupt testen, und wieso willst du
dynamic_cast
nicht dafür verwenden?
-
@wob sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
Dein Code funktioniert doch aber nicht für das Problem von @It0101, weil das is_base_of das nur zur Compilezeit kann. Wenn ich @It0101 richtig verstehe, ist doch genau dynamic_cast die Lösung (warum willst du das nicht?).
Ah, so war das gemeint. Also jede andere Lösung würde Performance-technisch keinen großen Unterschied machen.
@hustbaer sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
Und die letzte offensichtliche Variante wäre dann noch eine virtuelle Funktion in der Basisklasse einzubauen.
Ist vermutlich die eleganteste Lösung. Oder du baust dir zum Testen ein Concept (wie in meinem Concept Tutorial) und prüfst auf spezifische Funktionen in der Basisklasse. Aber spätestens bei der Zuweisung wirst du um ein
dynamic_cast
nicht drumrum kommen.
-
@VLSI_Akiko sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
Oder du baust dir zum Testen ein Concept (wie in meinem Concept Tutorial) und prüfst auf spezifische Funktionen in der Basisklasse. Aber spätestens bei der Zuweisung wirst du um ein dynamic_cast nicht drumrum kommen.
Wie willst du mit Concepts den dynamischen Typ eines Objekts prüfen? Also ich verstehe zumindest nicht was du meinst.
-
@hustbaer sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
Wie willst du mit Concepts den dynamischen Typ eines Objekts prüfen? Also ich verstehe zumindest nicht was du meinst.
Moment, lass mich ein Beispiel basteln. Kann natürlich sein, dass mir meine Fantasie auch nur etwas vorspielt, eine Idee habe ich jedenfalls.
-
Okay, also mir kam spontan das hier in den Sinn.
#include <iostream> #include <type_traits> struct Base1 { void test(); virtual ~Base1() = default; }; struct Base2 { virtual ~Base2() = default; }; struct Derived1 : public Base1 {}; struct Derived2 : public Base2 {}; Base1 *bptr1 = new Derived1; Base2 *bptr2 = new Derived2; Derived1 *dptr1 = new Derived1; Derived2 *dptr2 = new Derived2; template <typename T> constexpr bool hasTest(T *) { constexpr bool has_test = requires (T *t){ t->test(); }; if constexpr (has_test) return true; else return false; } // beziehungsweise in ganz kurz template <typename T> constexpr bool hasTest(T *) noexcept { return requires (T *t){ t->test(); }; } int32_t main() { std::cout << "base1: " << hasTest(bptr1) << "\n" // true << "derived1: " << hasTest(dptr1) << "\n" // true << "base2: " << hasTest(bptr2) << "\n" // false << "derived2: " << hasTest(dptr2) << std::endl; // false return 0; }
Als Running-Gag darfst du auch noch ein
noexcept
an das Template machen.
-
Der Hintergrund ist quasi:
class BaseType { }; class DerivedType : public BaseType { }; class BaseHolder { virtual bool isCorrectType( std::shared_ptr<BaseType> ) = 0; }; template <typename T> class DerivedHolder { virtual bool isCorrectType( std::shared_ptr<BaseType> task_ ) override { return std::dynamic_pointer_cast<T> ( task_ ) != nullptr; } };
Ich weiß nicht wie ich das erklären soll. Ich habe eine HolderKlasse die eigentlich nur den Typ T registrieren soll. Und dann kommen BaseTypes an, die abgeprüft werden ob T eine Ableitung von ihnen ist. Es ist ne Art Type-Registry. In Abhängigkeit vom Ergebnis von "isCorrectType" wird dem Nutzer der Klasse dann eine Art Event generiert, was ihn über das Auftreten eines Typs T informiert.
Und in einem Fall bekomme ich beim dynamic_pointer_cast eine "undefined reference in typeinfo" für den dynamic_cast weil T scheinbar nicht sauber aufgelöst werden konnte. Daher wollte ich das elegant umgehen.
META-Programmierung ist hier natürlich Blödsinn, weil die Entscheidung ja nicht zur CompileTime getroffen werden kann...
-
Hmm, versucht du so etwas wie ein Container zu bauen, der im Prinzip alle mögliche Typen speichern kann?
-
@VLSI_Akiko sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
Hmm, versucht du so etwas wie ein Container zu bauen, der im Prinzip alle mögliche Typen speichern kann?
Im Grunde ja. Die Objekte sind mir sogar egal. Ich will eigentlich nur die Typen speichern, um im Bedarfsfall auf Informationen zu diesem Typ reagieren zu können. Ich bekomme aber nie typisierte Infos sondern immer nur in Form der BasisKlasse des Typen.
-
Schon mal ein boost::variant oder die leicht abgespeckte C++17 std::variant probiert? Eventuell machen die ja schon das was du möchtest. Ansonsten könnte man sich davon auch einfach inspirieren lassen.
-
@It0101 Naja, verpass deinen Klassen mal virtuelle Funktionen, dann wird vermutlich auch das mit
dynamic_cast
klappen.@VLSI_Akiko Es geht um den dynamischen Typ, Akiko-Chan, nicht um den statischen. Mit dem statischen ist das natürlich einfach. Mit oder ohne Concepts.
-
@VLSI_Akiko sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
oder die leicht abgespeckte C++17 std::variant probiert?
Habe ich auch gedacht. Evt. auch mit std::holds_alternative().
-
@Quiche-Lorraine sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
std::variant
Das STL-Variant habe ich noch nie benutzt. Ich werde mich dazu mal belesen. Danke euch für den Tipp.
-
@hustbaer sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
@VLSI_Akiko Es geht um den dynamischen Typ, Akiko-Chan, nicht um den statischen. Mit dem statischen ist das natürlich einfach. Mit oder ohne Concepts.
Ja, ich habe es mittlerweile gemerkt.
@Quiche-Lorraine sagte in Ermitteln ob die Instanz einer Basisklasse in Wirklichkeit vom Typ T ( abgeleitet von Basis ) ist:
Habe ich auch gedacht. Evt. auch mit std::holds_alternative().
Dazu kann ich nicht wirklich viel sagen. Ich hatte bisher kein Code (fremd- oder selbstgeschrieben), wo diese Komponenten benutzt wurden. Ich weiß nur dass sie existieren, wofür sie da sind und dass die Boost Versionen komplexer sind, aber darüber hinaus kenne ich mich damit nicht aus.
-
Es gibt ein Video (C++ seasoning)von Sean Parent, in dem er sich mit Laufzeit Polymorphismus befasst, vielleicht kannste dir da ja was abgucken.