45 Stimmen

schablonenhaftes typedef?

Ich verwende libgc, einen Garbage Collector für C und C++. Um STL-Container garbage collectible zu machen, muss man den gc_allocator verwenden.

Anstatt zu schreiben

std::vector<MyType> 

muss man schreiben

std::vector<MyType,gc_allocator<MyType> >

Könnte es eine Möglichkeit geben, etwas zu definieren wie

template<class T> typedef std::vector<T,gc_allocator<T> > gc_vector<T>;

Ich habe mich vor einiger Zeit erkundigt und festgestellt, dass dies nicht möglich ist. Aber vielleicht habe ich mich geirrt oder es gibt eine andere Möglichkeit.

Die Definition von Karten auf diese Weise ist besonders unangenehm.

std::map<Key,Val> 

wird

std::map<Key,Val, std::less<Key>, gc_allocator< std::pair<const Key, Val> > >

EDIT: Nachdem ich die Verwendung des Makros ausprobiert habe, habe ich herausgefunden, dass der folgende Code es kaputt macht:

#define gc_vector(T) std::vector<T, gc_allocator<T> >
typedef gc_vector( std::pair< int, float > ) MyVector;

Das Komma innerhalb der Definition des Schablonentyps wird als Makroargumenttrennzeichen interpretiert.

Es scheint also, dass die innere Klasse/das innere Konstrukt die beste Lösung ist.

Hier ein Beispiel dafür, wie es in C++0X gemacht wird

// standard vector using my allocator
template<class T>
using gc_vector = std::vector<T, gc_allocator<T> >;

// allocates elements using My_alloc
gc_vector <double> fib = { 1, 2, 3, 5, 8, 13 };

// verbose and fib are of the same type
vector<int, gc_vector <int>> verbose = fib;

0 Stimmen

Warum sagen Sie, dass die TypeHelper Lösung erfordert die Neudefinition von Konstruktoren? Es ist nichts weiter als eine Abkürzung auf einem typedef...

0 Stimmen

Ups, Sie haben recht. Es ist gut, dass Sie dies in Ihrer Antwort klargestellt haben. Mir wurde gesagt, dass C++0X durch die Verwendung des Schlüsselworts "using" eine bessere Lösung bietet. Wissen Sie, wie das funktionieren würde?

1 Stimmen

C++0x wird Template-Aliase bereitstellen, mit denen man genau das tun kann, was man will. open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf

99voto

Felix Glas Punkte 14227

Sie können C++11-Templated-Type-Aliasing verwenden, indem Sie using z.B. so

template <typename T>
using gc_vector = std::vector<T, gc_allocator<T>>;

Hinweis: Ich weiß, dass dies eine alte Frage ist, aber da sie ziemlich viele Bewertungen erhalten hat und in den Suchergebnissen auftaucht, dachte ich, sie verdiene eine aktualisierte Antwort.

37voto

Paolo Tedesco Punkte 52228

Sie können kein "templated typedef" verwenden, aber Sie können eine Komfortklasse/ein Konstrukt mit einem inneren Typ verwenden:

template<typename T>
struct TypeHelper{
    typedef std::vector<T,gc_allocator<T> > Vector;
};

und verwenden Sie dann in Ihrem Code

TypeHelper<MyType>::Vector v;
TypeHelper<MyType>::Vector::iterator it;

Und etwas Ähnliches für die Karte:

template<typename K,typename V>
struct MapHelper{
    typedef std::map<K, V, gc_allocator<K,V> > Map;
};

EDIT - @Vijay: Ich weiß nicht, ob es noch eine andere Möglichkeit gibt, so würde ich es machen; ein Makro könnte eine kompaktere Notation ergeben, aber ich persönlich würde es nicht mögen:

#define GCVECTOR(T) std::vector<T,gc_allocator<T> >

EDIT - @chmike: Bitte beachten Sie, dass die TypeHelper Lösung nicht erfordern die Neudefinition von Konstruktoren!

1 Stimmen

Ja, die gibt es. Die Vererbung ist gerade in diesem Fall viel sauberer.

1 Stimmen

@sharptooth: Es stimmt, dass die Vererbung in diesem Fall die beste Notation bietet und die Verwendung eines Makros vermeidet, aber ihre Verwendung ist umstritten, wie in den Kommentaren zu Ihrer Antwort hervorgehoben wurde.

0 Stimmen

Ich warte geduldig auf Beweise für mögliche Probleme.

9voto

sharptooth Punkte 162790

Sie können öffentlich vererben:

template<class T>
class gc_vector<T> : public std::vector<T, gc_allocator<T> >
{
    public:
    // You'll have to redeclare all std::vector's constructors here so that
    // they just pass arguments to corresponding constructors of std::vector
};

Damit ist Ihr Problem vollständig gelöst. Der abgeleitete Typ kann überall dort verwendet werden, wo der Basistyp verwendet werden kann, und es gibt keinen Implementierungs-Overhead mit jedem anständigen Compiler.

Die Tatsache, dass std::vector einen nicht-virtuellen Destruktor hat, könnte zu undefiniertem Verhalten gemäß dem C++-Standard führen, wenn Sie versuchen, eine abgeleitete Klassenvariable durch einen Zeiger auf eine Basisklassenvariable zu löschen.

In der realen Welt sollte dies in diesem speziellen Fall keine Rolle spielen - die abgeleitete Klasse hat im Vergleich zur Basisklasse nichts Neues hinzugefügt und daher ruft der Destruktor für die abgeleitete Klasse einfach den Destruktor für die Basisklasse auf. Gehen Sie mit Paranoia vor, portieren Sie trotzdem vorsichtig.

Wenn Sie niemals Variablen dieser Klasse auf dem Heap zuweisen (und es ist typisch, Vektorvariablen auf dem Stack und als Mitglieder anderer Klassen zuzuweisen), betrifft das Problem des nicht-virtuellen Destruktors Sie nicht.

0 Stimmen

Es besteht das Problem, dass Vektoren nicht dafür ausgelegt sind, abgeleitet zu werden, da ihnen ein virtueller Destruktor fehlt.

0 Stimmen

Stimmt, aber in diesem speziellen Fall spielt das keine Rolle, da der abgeleitete Typ genau denselben Satz von Mebervariablen hat wie der Basistyp.

0 Stimmen

Es spielt eine Rolle, ob er einen Zeiger auf eine Basis löscht, die auf eine abgeleitete Basis verweist, da er sich dann im Bereich des undefinierten Verhaltens bewegt.

1voto

Zack Yezek Punkte 191

Es kann mit einem MACRO gemacht werden, wenn Sie bereit sind, Ihren Compiler bis an seine Grenzen zu bringen. Ich habe es bei der Implementierung von C++-Äquivalenten für die Java-Klassen "Future" und "Callable" getan. Unsere Bibliothek verwendet referenzgezählte Objekte, daher ist "Reference<T>" selbst eine Template-Klasse, in der "T" von "ReferencedObject" abgeleitet ist.

1. Create your template Classes. Mine are:

    template<typename T>
    class Callable {
    private:

    public:
        virtual T Call() = 0;
    };

    template<typename T> CountedFuture : public ReferencedObject {
    private:
       Callable<T>* theTask;
       T            theResult;

    public:
       T Get() { 
          // Run task if necessary ...
          if(task) {
             theResult = theTask->Call();
             delete theTask;
          }
          return theResult;
       }
    };

2. In the application code I'm using references, so I define the macro:

   #define Future(T) Reference<CountedFuture<T>>

Das Schöne daran ist, dass das Makro genau das tut, was man sich von einem "Template-Typedef" wünscht. Die Nachteile sind, dass man kein "<>" für den/die Typparameter verwenden kann und dass es keine Typinferenz gibt.

3. I can now use the Macro wherever I would use a template, like in functions:

   Future(char*) DoSomething() { ... }
   bool          TestSomething(Future(std::string) f) { .... }

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