Template member function spezialisieren



  • Hallo,

    ich habe eine Klasse

    class A
    {
    	template<typename R> R f() const;
    };
    

    und würde gerne f() so spezialisieren, dass obige Definition äquivalent zu

    class A
    {
    	int f() const { return 0; }
    };
    

    wird. Ich habe es mit

    template<> int A::f<int>() { return 0; }
    

    versucht, erhalte aber die Fehlermeldung

    ../src/scratch.cpp:3: error: template-id ‘f<int>’ for ‘int A::f()’ does
    make: *** [src/scratch.o] Error 1
        not match any template declaration
    

    Sinn und Zweck des Ganzen soll sein, Funktionen bezüglich ihres Rückgabewertes zu überladen. Also zusätzlich noch sowas wie

    template<> double A::f<double>() { return 1; }
    

    zu haben und dann

    A a;
    a.f<int>();
    a.f<double>();
    

    schreiben zu können.



  • ingobulla schrieb:

    Sinn und Zweck des Ganzen soll sein, Funktionen bezüglich ihres Rückgabewertes zu überladen.

    Wozu das? Für einzelne Fälle kann das sinnvoll sein (wobei es sich bei dir nicht um Überladung handelt, da diese implizit geschieht), doch im Allgemeinen ist ein Cast auf User-Seite wohl angebrachter. Oder?



  • Versuchst du f<int> in scratch.cpp zu spezialisieren, während die Deklaration in der Klassedefinition in scretch.hpp ist?



  • Soweit ich weiss dürfen member Funktionen nicht spezialisiert werden.



  • Klar darf man Methodenvorlagen spezialisieren. Das Problem in diesem Fall ist, dass die Signatur nicht übereinstimmt.

    template<> int A::f<int>() /* --> */ const /* <-- */ { return 0; }
    


  • Ich kann nur raten allgemein vom Spezialisieren von Funktionen die Finger zu lassen, und stattdessen Helper-Klassen-Templates mit statischen Funktionen zu verwenden.

    Das Funktions-Template leitet dann einfach an die statische Funktion des Helper-Klassen-Templates weiter, und spezialisiert wird immer nur die Klasse.



  • Why Not Specialize Function Templates?: http://www.gotw.ca/publications/mill17.htm



  • Er spezialisiert hier aber den Rückgabetyp. Die Methode f kann man gar nicht überladen (ohne ihr zusätzliche Argumente zu verpassen). Ansonsten ist es aber richtig, dass man überladen sollte statt zu spezialisieren.



  • brotbernd schrieb:

    Er spezialisiert hier aber den Rückgabetyp. Die Methode f kann man gar nicht überladen (ohne ihr zusätzliche Argumente zu verpassen). Ansonsten ist es aber richtig, dass man überladen sollte statt zu spezialisieren.

    Und wenn man sie so aufruft?

    A a;
    cout << a.f<double>() << "\n";
    cout << a.f<int>()<< "\n";
    


  • brotbernd schrieb:

    Er spezialisiert hier aber den Rückgabetyp. Die Methode f kann man gar nicht überladen (ohne ihr zusätzliche Argumente zu verpassen).

    Ich weiss nicht was du jetzt mit "zusätzliche Argumente zu verpassen" meinst, Funktions-Parameter oder Template-Parameter. Es geht auf jeden Fall beides so weit ich weiss:

    #include <vector>
    
    class A
    {
    public:
    
    	template <class R>
    	R f()
    	{
    		return R();
    	}
    
    	// overload
    	template <class R, size_t N>
    	std::vector<R> f()
    	{
    		return std::vector<R>(N);
    	}
    
    	// another overload
    	template <class R>
    	std::vector<R> f(size_t N)
    	{
    		return std::vector<R>(N);
    	}
    };
    
    int main(int argc, char** argv)
    {
    	A a;
    
    	int i = a.f<int>();
    	std::vector<int> v1 = a.f<int, 10>();
    	std::vector<int> v2 = a.f<int>(10);
    
    	return 0;
    }
    


  • wxSkip schrieb:

    brotbernd schrieb:

    Er spezialisiert hier aber den Rückgabetyp. Die Methode f kann man gar nicht überladen (ohne ihr zusätzliche Argumente zu verpassen). Ansonsten ist es aber richtig, dass man überladen sollte statt zu spezialisieren.

    Und wenn man sie so aufruft?

    A a;
    cout << a.f<double>() << "\n";
    cout << a.f<int>()<< "\n";
    

    Was soll dann sein, ich verstehe die Frage nicht. 😕



  • Danke für all die Rückmeldungen.

    Ich denke, ich sollte hier mal das eigentliche Problem schildern (es scheint mir wahrscheinlich häufiger aufzutreten, daher wird jemand wohl wissen, mit welcher Technik es am besten zu lösen ist).

    Ich habe zwei Template-Klassen

    template<class N> class A
    {
    public:
    	virtual N* unspec_node() const = 0;
    	...
    };
    
    template<class N, class ON> class B : public virtual A<N>
    {
    private:
    	ON* m_node;
    public:
    	ON* node() const { return m_node; }
    	N* unspec_node() const { return m_node; }
    	...
    };
    

    Das Problem ist nun, dass ich in der Klasse B eine Methode node() habe, die einen Knoten zurückgibt, und eine Methode unspec_node(), die einen unspezifischen Knoten zurückgibt (ON ist aus N abgeleitet). Ich frage mich nun, ob man die Benennung der Methoden konsistenter machen kann, dass ich statt

    B<C_N, C_ON> b;
    b->node();
    b->unspec_node()
    

    irgendwas verständlicheres in der Richtung

    B<C_N, C_ON> b;
    b->node<C_N>();
    b->node<C_ON>();
    

    schreiben kann.



  • Du kannst es einfach so machen:

    template<class N> class A
    {
    public:
        virtual N* node() const = 0;
        ...
    };
    
    template<class N, class ON> class B : public virtual A<N>
    {
    private:
        ON* m_node;
    public:
        ON* node() const { return m_node; } // implementiert A::node, wenn ON von N abgeleitet ist!
        ...
    };
    

    C++ kann covariant return values bei virtuellen Funktionen (ein oft übersehenes Feature).

    MSVC 6 frisst solchen Code noch nicht, ab MSVC 7.0 (2002) geht es dann aber.



  • hustbaer schrieb:

    Du kannst es einfach so machen:

    template<class N> class A
    {
    public:
        virtual N* node() const = 0;
        ...
    };
    
    template<class N, class ON> class B : public virtual A<N>
    {
    private:
        ON* m_node;
    public:
        ON* node() const { return m_node; } // implementiert A::node, wenn ON von N abgeleitet ist!
        ...
    };
    

    C++ kann covariant return values bei virtuellen Funktionen (ein oft übersehenes Feature).

    MSVC 6 frisst solchen Code noch nicht, ab MSVC 7.0 (2002) geht es dann aber.

    Danke, dass wusste ich noch nicht. Allerdings bekomme ich, wenn ich das bei mir versuche, die Fehlermeldung (relevanter Teil nach der Leerzeile)

    ../src/node_container/base_nc.h: In instantiation of ‘NC_with_1_Nd<
            Sim_Nd, Sim_Tip_Nd, Sim_NC, Sim_Intern_NC, Sim_Tree>’:
    ../src/node_container/base_nc.h:204:   instantiated from ‘Tip_NC<
            Sim_Nd, Sim_Tip_Nd, Sim_NC, Sim_Intern_NC, Sim_Tree>’
    ../src/node_container/sim_nc.h:44:   instantiated from here
    
    ../src/node_container/base_nc.h:140: error: invalid covariant return type for
        ‘Sim_Tip_Nd * NC_with_1_Nd<
            Sim_Nd, Sim_Tip_Nd, Sim_NC, Sim_Intern_NC, Sim_Tree>::node() const’
    ../src/node_container/base_nc.h:120: error:   overriding ‘Sim_Nd *
        NC_with_1_abstract_Nd<Sim_Nd, Sim_NC, Sim_Intern_NC, Sim_Tree>::node()
        const’
    

    Hierbei sehen meine Template-Klassen so aus

    template<class N, class C, class PC, class T> class NC_with_1_abstract_Nd : public virtual Base_NC<N, C, PC, T>
    {
    public:
    	virtual N* node() const = 0;
    	…
    };
    
    template<class N, class ON, class C, class PC, class T> class NC_with_1_Nd
    : public virtual NC_with_1_abstract_Nd<N, C, PC, T>, public virtual NC_with_Nds<N, ON, C, PC, T>
    {
    private:
    	ON* m_node;
    public:
    	ON* node() const { return m_node; }
    	…
    }
    

    Des Weiteren ist Sim_Tip_Nd aus Sim_Nd abgeleitet.

    Weiss jemand, was hier los ist?



  • seldon schrieb:

    Klar darf man Methodenvorlagen spezialisieren. Das Problem in diesem Fall ist, dass die Signatur nicht übereinstimmt.

    template<> int A::f<int>() /* --> */ const /* <-- */ { return 0; }
    

    Ich glaube nicht... hab den Auszug aus dem Standard grad nicht parat, aber ich hab´s auch schon mehrmals (Stichwort: Esel, Stein, stossen) probiert 😉
    Folgender Code kompiliert z.B. nicht:

    #include <string>
    
    using namespace std;
    
    struct Test
    {
    	template<typename T>
    	int func( const T& op )
    	{
    		return 0;
    	}
    
    	template<>
    	int func<std::string>( const std::string& op )
    	{
    		return 1;
    	}
    };
    
    int main()
    {
       string s = "Hello World";
    
       Test t;
       int r0 = t.func( 0 );
       int r1 = t.func( s );
    }
    

    Compiler Ausgabe:

    Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
    Copyright 1988-2008 Comeau Computing.  All rights reserved.
    MODE:strict errors C++ C++0x_extensions
    
    "ComeauTest.c", line 13: error: explicit specialization is not allowed in the
              current scope
      	template<>
    


  • @ingobulla:
    Versuch ein vollständiges, compilierbares Minimalbeispiel zu bauen wo der Fehler auftritt (OK, in dem Fall *nicht* compilierbar, aber es sollte bis auf den einen Fehler compilieren).

    BTW: ich schätze dass Sim_Tip_Nd eben doch nicht (public) von Sim_Nd abgeleitet ist.



  • hustbaer schrieb:

    @ingobulla:
    Versuch ein vollständiges, compilierbares Minimalbeispiel zu bauen wo der Fehler auftritt (OK, in dem Fall *nicht* compilierbar, aber es sollte bis auf den einen Fehler compilieren).

    BTW: ich schätze dass Sim_Tip_Nd eben doch nicht (public) von Sim_Nd abgeleitet ist.

    Ich habe den Fehler gefunden: Es fehlte ein passender Include, so dass dort, wo es nötig gewesen wäre, nicht bekannt war, dass Sim_Tip_Nd aus Sim_Nd abgeleitet ist.

    Ich hätte noch eine Anschlussfrage, die werde ich aber aus Lesbarkeitsgründen als neuen Beitrag formulieren.


Log in to reply