Instantiierung einer statischen Klassenvariable eines Templates



  • Folgendes Beispiel illustriert mein Problem:

    template <class C>
    	class MyHelperBase
    {
    	struct instantiator_t
    	{
    		instantiator_t (void) {}
    	};
    
    	static instantiator_t instantiator;
    };
    
    class MyClass
    {
    protected:
    	class MyHelper : private MyHelperBase <MyHelper>
    	{
    	};
    };
    
    template <> MyHelperBase <MyClass::MyHelper>::instantiator_t
    	MyHelperBase <MyClass::MyHelper>::instantiator;
    

    In den letzten Zeilen beklagen sich MSVC und Comeau, daß kein Zugriff auf MyClass::MyHelper besteht (was ja an sich korrekt ist). Nur weiß ich nicht, wie ich die statische Variable anders instantiieren könnte. Muß ich MyHelper nun doch public machen, oder ist die Instantiierung irgendwie aus dem Kontext von MyClass heraus möglich?


  • Mod

    audacia schrieb:

    Folgendes Beispiel illustriert mein Problem:

    template <class C>
    	class MyHelperBase
    {
    	struct instantiator_t
    	{
    		instantiator_t (void) {}
    	};
    
    	static instantiator_t instantiator;
    };
    
    class MyClass
    {
    protected:
    	class MyHelper : private MyHelperBase <MyHelper>
    	{
    	};
    };
    
    template <> MyHelperBase <MyClass::MyHelper>::instantiator_t
        MyHelperBase <MyClass::MyHelper>::instantiator;
    

    In den letzten Zeilen beklagen sich MSVC und Comeau, daß kein Zugriff auf MyClass::MyHelper besteht (was ja an sich korrekt ist). Nur weiß ich nicht, wie ich die statische Variable anders instantiieren könnte. Muß ich MyHelper nun doch public machen, oder ist die Instantiierung irgendwie aus dem Kontext von MyClass heraus möglich?

    Hier definierst du instantiator nur für eine bestimmte Spezialisierung des MyHelperBase-Templates. Eine andere Möglichkeit ist die Definition des Members für das gesamte Template - damit umgehst du die Zugriffskontrolle bei den Templateargumenten (und die Definition darf auch im Header stehen).

    template <class C> typename MyHelperBase <C>::instantiator_t MyHelperBase <C>::instantiator;
    

    Etwas schwieriger wird es, wenn noch ein initialisierender Ausdruck dazukommen soll - eine Möglichkeit wäre, hierfür eine extra Templatefunktion zu benutzen und diese für die tatsächlich vorkommenden Templateargumente zu spezialisieren und ggf. als friend von MyCass zu deklarieren (oder sie ist gleich ein statischer Member).



  • camper schrieb:

    Hier definierst du instantiator nur für eine bestimmte Spezialisierung des MyHelperBase-Templates. Eine andere Möglichkeit ist die Definition des Members für das gesamte Template - damit umgehst du die Zugriffskontrolle bei den Templateargumenten (und die Definition darf auch im Header stehen).

    Das wäre natürlich die naheliegendste Lösung - aber leider funktioniert es nicht. Oder wird bei dir in folgendem Beispiel instantiator_t::instantiator_t() erreicht?

    #include <iostream>
    
    template <class C>
    	class MyHelperBase
    {
    	struct instantiator_t
    	{
    		instantiator_t (void) { std::cout << "Here I am" << std::endl; }
    	};
    
    	static instantiator_t instantiator;
    };
    
    template <class C>
    	typename MyHelperBase <C>::instantiator_t MyHelperBase <C>::instantiator;
    
    class MyClass
    {
    protected:
    	class MyHelper : private MyHelperBase <MyHelper>
    	{
    	};
    };
    
    int main (void)
    {
    }
    

    Woran das liegt, weiß ich nicht. Jedenfalls verhalten sich MSVC und BCC hier gleich ruhig.


  • Mod

    audacia schrieb:

    Das wäre natürlich die naheliegendste Lösung - aber leider funktioniert es nicht. Oder wird bei dir in folgendem Beispiel instantiator_t::instantiator_t() erreicht?

    Natürlich nicht. Für welche C denn? Instantiieren musst du es natürlich noch (eine implizite Instantiierung einer Templateklasse instantiiert ja nicht statische Member) - das ist aber nicht an so strikte Vorausetzungen (Die Definition muss ja in einem umliegenden Namensraum erfolgen) gebunden und insofern einfach genug.



  • camper schrieb:

    (eine implizite Instantiierung einer Templateklasse instantiiert ja nicht statische Member)

    Gut zu wissen; das erklärt dieses Verhalten.

    camper schrieb:

    das ist aber nicht an so strikte Vorausetzungen (Die Definition muss ja in einem umliegenden Namensraum erfolgen) gebunden und insofern einfach genug.

    Das versuche ich nun schon eine ganze Weile, bisher ohne Erfolg. Wie geht das denn so einfach?



  • Ich habe es jetzt gelöst, indem ich MyHelperBase <MyHelper> innerhalb von MyClass als friend deklariert habe. Das ist zwar suboptimal, aber etwas besseres finde ich momentan nicht.


  • Mod

    audacia schrieb:

    camper schrieb:

    das ist aber nicht an so strikte Vorausetzungen (Die Definition muss ja in einem umliegenden Namensraum erfolgen) gebunden und insofern einfach genug.

    Das versuche ich nun schon eine ganze Weile, bisher ohne Erfolg. Wie geht das denn so einfach?

    Ein statischer Member wird implizit instantiiert, wen er in einer Weise benutzt wird, die die Existenz der Definition voraussetzt. Der einfachste Weg, der mir dazu einfällt, ist die Bestimmung der Adresse des Members.
    Um die Adresse eines Objektes bestimmen zu können, muss dieses existieren, mithin definiert worden sein.
    Das können wir ausnutzen, falls wir irgendeine Funktion haben, die sowieso implizit instantiiert wird. Eine nahe liegende Wahl ist der Destruktor - falls wir irgendwann einmal ein Objekt der Klasse benötigen, wird es sicherlich auch irgendwo zerstört werden, und dabei wird der Destruktor ggf. implizit instantiiert, einfach einen Ausdruck einfügen, der die Adresse bestimmt (dieser hat im Übrigen keine Seiteneffekte und erzeugt trivialerweise keinen Overhead - das bedeutet aber nicht, dass hier keine Nutzung vorliegt und der Compiler die Instantiierung einfach wegoptimieren darf):

    #include <iostream>
    
    template<typename T>
    struct foo {
        ~foo() { &x; }  // <<==, instantiiert implizit x
        static foo<T> x;
    };
    
    template<typename T>
    foo<T> make_foo() { std::cout << "make_foo\n"; return foo<T>(); }
    
    template<typename T>
    foo<T> foo<T>::x = make_foo<T>();
    
    int main()
    {
        foo<int> a; // instantiiert implizit (neben anderen) foo<int>::~foo
    }
    

Anmelden zum Antworten