2 Stimmen

C++-Spezialisierung, type_of oder einfach typeid

Ich würde gerne wissen, was in meiner Situation besser ist und warum. Zunächst einmal habe ich gehört, dass die Verwendung von RTTI (typeid) schlecht ist. Kann mir jemand erklären, warum? Wenn ich die Typen genau kenne, was ist falsch daran, sie in einer Laufzeit zu vergleichen? Gibt es außerdem ein Beispiel für die Verwendung von boost::type_of? Ich habe beim Durchsuchen des mächtigen Google keines gefunden :) Eine andere Lösung für mich ist die Spezialisierung, aber ich müsste mindestens 9 Typen einer neuen Methode spezialisieren. Hier ist ein Beispiel, was ich brauche:

Ich habe diese Klasse

  template<typename A, typename B, typename C>
  class CFoo
  {
     void foo()
     {
       // Some chunk of code depends on old A type
     }

  }

Also muss ich eher in typeid überprüfen (was ich gehört habe, ist BAD) und diese 3 Realisierungen in Beispiel wie machen:

 void foo()
   {
      if (typeid(A) == typeid(CSomeClass)
       // Do this chunk of code related to A type
      else
      if (typeid(B) == typeid(CSomeClass)
       // Do this chunk of code related to B type
      else
      if (typeid(C) == typeid(CSomeClass)
       // Do this chunk of code related to C type
   }

Was ist also die beste Lösung? Ich möchte nicht für alle A, B, C zu spezialisieren, weil jeder Typ 3 Spezialisierungen hat, so dass ich 9 Methoden oder nur diese typeid Prüfung erhalten.

6voto

sellibitze Punkte 26706

Es ist schlecht, weil

  1. A, B und C sind zur Kompilierzeit bekannt, aber Sie verwenden einen Laufzeitmechanismus. Wenn Sie typeid aufrufen, sorgt der Compiler dafür, dass Metadaten in die Objektdateien aufgenommen werden.
  2. Wenn Sie "Do this chunk of code related to A type" durch tatsächlichen Code ersetzen, der die Schnittstelle von CSomeClass nutzt, werden Sie sehen, dass Sie den Code nicht kompilieren können, wenn A!=CSomeClass und A eine inkompatible Schnittstelle haben. Der Compiler versucht trotzdem, den Code zu übersetzen, obwohl er nie ausgeführt wird. (siehe Beispiel unten)

Normalerweise gliedern Sie den Code in separate Funktionsvorlagen oder statische Mitgliedsfunktionen von Klassen aus, die spezialisiert werden können.

Schlecht:

template<typename T>
void foo(T x) {
    if (typeid(T)==typeid(int*)) {
        *x = 23; // instantiation error: an int can't be dereferenced
    } else {
        cout << "haha\n";
    }
}
int main() {
    foo(42); // T=int --> instantiation error
}

Besser:

template<typename T>
void foo(T x) {
    cout << "haha\n";
}
void foo(int* x) {
    *x = 23;
}
int main() {
    foo(42); // fine, invokes foo<int>(int)
}

Prost, s

4voto

Goz Punkte 59671

Im Allgemeinen können Lösungen ohne RTTI gefunden werden. Es "kann" zeigen, dass Sie das Design der Software nicht richtig durchdacht haben. DAS ist schlecht. Manchmal ist RTTI peut aber eine gute Sache sein.

Nichtsdestotrotz ist das, was Sie tun wollen, etwas merkwürdig. Könnten Sie nicht eine vorläufige Vorlage erstellen, die etwa wie folgt gestaltet ist:

template< class T > class TypeWrapper
{
  T t;
public:
  void DoSomething()
  {
  }
};

Dann spezialisieren Sie sich teilweise für die gewünschten Funktionen wie folgt:

template<> class TypeWrapper< CSomeClass >
{
  CSomeClass c;
public:
  void DoSomething()
  {
     c.DoThatThing();
  }
};

In Ihrer oben definierten Klasse würden Sie dann etwas tun wie ...

Vorlage

  class CFoo
  {
     TypeWrapper< A > a;
     TypeWrapper< B > b;
     TypeWrapper< C > c;
     void foo()
     {
       a.DoSomething();
       b.DoSomething();
       c.DoSomething();
     }

  }

Auf diese Weise wird im "DoSomething"-Aufruf nur dann tatsächlich etwas getan, wenn die teilweise spezialisierte Vorlage durchlaufen wird.

2voto

Christopher Punkte 8744

Das Problem liegt in den Code-Blöcken, die Sie für jede Spezialisierung schreiben.

Es spielt keine Rolle, ob Sie (der Länge nach) schreiben

void foo()
{
   if (typeid(A) == typeid(CSomeClass)
    // Do this chunk of code related to A type
   else
   if (typeid(B) == typeid(CSomeClass)
    // Do this chunk of code related to B type
   else
   if (typeid(C) == typeid(CSomeClass)
    // Do this chunk of code related to C type
}

oder

void foo()
{
   A x;
   foo_( x );
   B y;
   foo_( y );
   C z;
   foo_( z );
}
void foo_( CSomeClass1& ) {}
void foo_( CSomeClass2& ) {}
void foo_( CSomeClass3& ) {}

Der Vorteil des zweiten Falles ist, dass Sie beim Hinzufügen einer Klasse D vom Compiler daran erinnert werden, dass eine Überladung für foo_ fehlt, die Sie schreiben müssen. Dies kann bei der ersten Variante vergessen werden.

2voto

UncleBens Punkte 39755

Ich fürchte, das wird nicht von Anfang an funktionieren. Diese "Code-Blöcke" müssen kompilierbar sein, auch wenn der Typ nicht CSomeClass ist.

Ich glaube auch nicht, dass type_of helfen wird (wenn es dasselbe ist wie auto und decltype in C++0x).

Ich denke, Sie könnten diese drei Teile in separate Funktionen extrahieren und jede für CSomeClass überladen. (Bearbeiten: oh es gibt else if's . Dann brauchen Sie vielleicht tatsächlich viele Überladungen/Spezialisierungen. Wofür ist dieser Code?)

Edit2: Es scheint, dass Ihr Code hofft, das Äquivalent der folgenden zu tun, wo int der spezielle Typ ist:

#include <iostream>

template <class T>
bool one() {return false; }

template <>
bool one<int>() { std::cout << "one\n"; return true; }

template <class T>
bool two() {return false; }

template <>
bool two<int>() { std::cout << "two\n"; return true; }

template <class T>
bool three() {return false; }

template <>
bool three<int>() { std::cout << "three\n"; return true; }

template <class A, class B, class C>
struct X
{
    void foo()
    {
        one<A>() || two<B>() || three<C>();
    }
};

int main()
{
    X<int, double, int>().foo(); //one
    X<double, int, int>().foo();  //two
    X<double, double, double>().foo(); //...
    X<double, double, int>().foo(); //three
}

1voto

Douglas Leeder Punkte 50423

Ich glaube, Sie haben Ihre Abstraktionen irgendwo falsch verstanden.

Ich würde versuchen, A, B und C in Form von Schnittstellen neu zu definieren, die sie offenlegen müssen (abstrakte Basisklassen in C++ mit rein virtuellen Methoden).

Templating ermöglicht im Grunde Duck-Typing, aber es klingt, als wüsste CFoo zu viel über die Klassen A, B und C.

typeid ist schlecht, weil:

  1. typeid kann teuer sein, bläht auf Binärdateien, trägt zusätzliche Informationen, die nicht benötigt werden benötigt werden.
  2. Nicht alle Compiler unterstützen dies
  3. Damit wird die Klassenhierarchie durchbrochen.

Was ich empfehlen würde, ist ein Refactoring: Entfernen Sie das Templating, definieren Sie stattdessen Schnittstellen für A, B und C, und lassen Sie CFoo diese Schnittstellen übernehmen. Das wird Sie zwingen, das Verhalten zu überarbeiten, damit A, B und C tatsächlich zusammenhängende Typen sind.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X