2017-02-20 2 views
2

Es ist bekannt, dass static Variablen einer Routine, Funktion oder Methode ("Member-Funktion") exist for each unique instantiation.Erstellen von Wirkung der Methode Variable statische pro Instanz und pro umschließende Instanz


(. Quick Review und Plausibilitätsprüfung)

Im gemeinsamen Fall ist dies genau eine Variable:

int f() { 
    static int i = 0; 
    return i++; 
} 

Das heißt, es gibt eine einzelne Variable i die created in .BSS/.DATA ist, dass "gehört" zur Funktion f. Bei Vorlagen, dann ist es ein pro einmaliger Instantiierung:

template <typename T> int f() { 
    static int i = 0; 
    return i++; 
} 
int g() { 
    return f<int>() + f<int>() + f<int>() + f<float>(); 
} 

Hier gibt es zwei einzigartige instantiations (f<int> und f<float>), und so gibt es zwei i s im .bss/.DATA Segment.


Problem

Ich möchte eine Möglichkeit Variablen in Vorlagen-Element-Funktionen zu machen, leben beide pro Instanz und pro-Instanziierung ihrer umgebenden Klasse. Ich bin daran interessiert, diesen Effekt zu erreichen, indem mehr oder weniger leistungsfähige Mittel notwendig sind (möglicherweise überhaupt keine statischen). Wie soll ich das machen?

Zum Beispiel:

class Foo { public: 
    template <typename T> int f() { 
     static int i = 0; 
     return i++; 
    } 
}; 

void g() { 
    Foo foo1; 
    Foo foo2; 
    /* 
     These will have values a=0, b=0, c=1. This happens because there are two 
     variables: 
      "Foo::f<float>()::i" 
      "Foo::f<double>()::i" 
     What I *want* is for there to be three variables: 
      "Foo::f<float>()::i" (foo1's copy) 
      "Foo::f<float>()::i" (foo2's copy) 
      "Foo::f<double>()::i" (foo1's copy) 
     So the result is a=0, b=0, c=0. How can I accomplish this effect (maybe 
     not using static)? 
    */ 
    int a = foo1.f<float>(); 
    int b = foo1.f<double>(); 
    int c = foo2.f<float>(); 
} 
+2

Kann eine des downvoters bitte geben ein Grund? Scheint wie eine gut geschriebene Frage zu mir, obwohl möglicherweise ein XY-Problem ... – ildjarn

+0

Idk, warum es zwei nahe Stimmen hier gibt. Die Frage ist ziemlich klar (aber definitiv schwer zu beantworten). –

Antwort

2

Sie könnten eine Typenzuordnung für jede Instanz speichern. Hier nehme ich an, dass RTTI aktiviert ist, type_index zu verwenden, wenn Sie RTTI nicht verwenden können, überprüfen Sie Boost.TypeIndex oder Template metaprogram converting type to unique number für seinen Ersatz.

#include <unordered_map> 
#include <typeindex> 
#include <cstdio> 

class Foo { 
    std::unordered_map<std::type_index, int> _values; 

public: 
    template<typename T> 
    int f() { 
     int& i = _values[typeid(T)]; 
     return i++; 
    } 
}; 

int main() { 
    Foo foo1; 
    Foo foo2; 

    int a = foo1.f<float>(); 
    int b = foo1.f<double>(); 
    int c = foo2.f<float>(); 

    foo1.f<float>(); 

    int d = foo1.f<float>(); 
    int e = foo1.f<double>(); 
    int f = foo2.f<float>(); 

    printf("%d %d %d %d %d %d\n", a, b, c, d, e, f); 
    // prints 0 0 0 2 1 1 
} 
+0

@ildjarn Danke, bearbeitet in. – kennytm

+0

Alle aktuellen Antworten auf diese Frage nehmen im Wesentlichen diesen Ansatz mit ein paar Variationen. Ich akzeptiere diese Version, da ich denke, dass sie die sauberste ist. – imallett

3

Die einzige Möglichkeit, verschiedene Instanzen eines Objekts haben Sie unterschiedliche Ergebnisse zu geben, ist diese Objekte haben Membervariablen mit unterschiedlichen Werten haben. Der einfachste Ansatz ist nur zu haben, ein std::map von std::type_info:

struct TypeInfoCompare { 
    bool operator()(std::type_info const* lhs, std::type_info const* rhs) const { 
     return lhs->before(*rhs); 
    } 
}; 

struct Foo { 
    template <class T> 
    int f() { 
     return m[&typeid(T)]++; 
    } 

    std::map<std::type_info const*, int, TypeInfoCompare> m; 
}; 

Dies gibt Ihnen einen pro-Typ-Zähler, der für jede Instanz von Foo anders sein wird.


Die std::map könnte auch ein std::unordered_map und std::type_info::hash_code() als Hash verwenden sein.

1

Wie im Allgemeinen sollte man könnte sein, variable Vorlage typeid eine Alternative vermeiden. Natürlich kann die variable Vorlage nur statisch sein, so dass ein eindeutiger Wert für jede Instanz gespeichert wird, die eine variable Vorlagenzuordnung benötigt instance -> value.

#include <cassert> 
#include <map> 

struct Foo { 
    template <class T> static std::map<Foo *, int> m; 

    template <class T> int f() { 
     return m<T>[this]++; 
    } 
}; 

template <class T> std::map<Foo *, int> Foo::m; 


int main() { 
    Foo foo1; 
    Foo foo2; 
    assert(foo1.f<int>() == 0); 
    assert(foo1.f<int>() == 1); 
    assert(foo1.f<int>() == 2); 
    assert(foo1.f<float>() == 0); 
    assert(foo2.f<int>() == 0); 
    assert(foo2.f<int>() == 1); 
    assert(foo2.f<int>() == 2); 
    assert(foo2.f<float>() == 0); 
} 

[live demo]

Es könnte auch eine gute Idee, ersetzen std::map mit std::unordered_map (es wird keine weiteren Hasher erfordern - example)

Ansatz auch erfolgreich vor c angewendet werden kann ++ 14 als variable Vorlage kann einfach mit inner structure template ersetzt werden.

Warnung
Es muss festgestellt werden, dass ein Ansatz wird standardmäßig nicht tiefe Kopie klonen Wert für bestimmte Instanz unterstützen.


das Problem zu überwinden man noch ein wenig modifizierte Technik verwenden kann variable Vorlagen und Mapping (noch keine RTTI erforderlich) unter Verwendung von:

#include <cassert> 
#include <unordered_map> 

struct Foo { 
    template <class T> 
    static int label; 

    std::unordered_map<int *, int> m; 

    template <class T> int f() { 
     return m[&label<T>]++; 
    } 
}; 

template <class T> int Foo::label; 

int main() { 
    Foo foo1; 
    Foo foo2; 
    assert(foo1.f<int>() == 0); 
    assert(foo1.f<int>() == 1); 
    assert(foo1.f<int>() == 2); 
    assert(foo1.f<float>() == 0); 
    assert(foo2.f<int>() == 0); 
    assert(foo2.f<int>() == 1); 
    assert(foo2.f<int>() == 2); 
    assert(foo2.f<float>() == 0); 
} 

[live demo]

Verwandte Themen