Problem mit Template



  • Hi,

    ich habe ein Problem mit Template-Member-Functions.

    Dieser Code wird fehlerfrei kompiliert:

    template <typename T>
    std::string Convert(const std::wstring& w)
    {
    	std::string Result;
    	std::transform(w.begin(), w.end(), std::back_inserter(Result), T());
    	return Result;
    }
    void foo()
    {
    	Convert<StringConverter>(L"Hello World");
    }
    

    Das ist klar.

    Ich verstehe aber nicht, warum folgender Code nicht kompiliert:

    class A
    {
    public:
    	template <typename T>
    	std::string Convert(const std::wstring& w)
    	{
    		std::string Result;
    		std::transform(w.begin(), w.end(), std::back_inserter(Result), T());
    		return Result;
    	}
    };
    void foo()
    {
    	A a;
    	a.Convert<StringConverter>(L"Hello World");
    }
    

    Die Fehlermeldung des Compilers lautet:

    [...]\main.cpp(31) : error C2275: 'StringConverter' : illegal use of this type as an expression
            [...]\stringconverter.h(9) : see declaration of 'StringConverter'
    

    Wo ist in dem Code überhaupt ein Ausdruck, in dem StringConverter vorkommt? Ich habe das doch in template-Klammern geschrieben. 😕

    Mit dem gcc funktioniert dieser Code ohne Probleme, nur der VC++ meldet Fehler. Ich möchte das aber mit dem VC++ zum Laufen kriegen. Was muss ich tun?



  • Ich oute mich mal wieder als unwissend, aber kann es sein, dass du a.template Convert<StringConverter> schreiben musst? Der Compiler scheint < als kleiner-als Operator zu interpretieren, demzufolge auch StringConverter für einen Ausdruck zu halten.



  • Das hab ich auch schon probiert. Dann erscheinen aber folgende Fehler:

    [...]\main.cpp(31) : error C2951: template declarations are only permitted at global or namespace scope
    [...]\main.cpp(31) : error C2059: syntax error : 'newline'
    


  • btw und OT:
    Ist folgender Code erlaubt?

    a.A::Convert<StringConverter>(L"Hello World");
    

    Das führt beim VC++ nämlich zu einem "fatal error C1001: INTERNAL COMPILER ERROR". 🙄



  • Hai,

    die Fehlermeldung ist doch eigentlich hilfreich. Du must Dein

    template<typename T>
    

    vor

    class a
    

    setzen

    grüße Con@n



  • hm eigentlich sollte bashar recht haben... welche version hat denn dein vc?
    mit der 6.0 version hatte ich mal ähnliche probleme, da hat ein

    template std::string Convert<StringConverter> (const std::wstring &);
    

    geholfen. fragt mich nicht, warum 😃
    aber als abhilfe kannst du auch eine /zb struktur übergeben, die den typ des converters als typedef enthält, wie volkard mal gemacht hat



  • Bashar schrieb:

    Ich oute mich mal wieder als unwissend, aber kann es sein, dass du a.template Convert<StringConverter> schreiben musst? Der Compiler scheint < als kleiner-als Operator zu interpretieren, demzufolge auch StringConverter für einen Ausdruck zu halten.

    Nein. Der Aufruf findet in einer normalen Funktion statt. Die Qualifikation mit typename bzw. template darf aber ausschließlich in Templates passieren.

    welche version hat denn dein vc?

    Das ist die entscheidene Frage. Der VC 6.0 unterstützt die explizite Angabe von Templateparametern nur bei freien Funktionen (und da fehlerhaft), nicht aber bei Memberfunktionen.



  • @Con@n:
    Es gibt einen gravierenden Unterschied zwischen template-Klassen und template-member-functions. Ich brauche eine template-member-function.

    davie:
    Ich hab die VC++ 6.

    template std::string Convert<StringConverter> (const std::wstring &);
    

    ähm... Wo soll ich das hinschreiben? Sieht aus wie eine Deklaration, aber wohin damit? 😕

    "Dummy-Parameter"
    Meinst du sowas:

    class A
    {
    public:
    	template <typename T>
    	std::string Convert(const std::wstring& w, const T& Dummy)
    	{
    		std::string Result;
    		std::transform(w.begin(), w.end(), std::back_inserter(Result), T());
    		return Result;
    	}
    };
    
    void foo()
    {
    	A a;
    	a.Convert(L"Hello World", StringConverter());
    }
    

    Ich kann aber Dummy nicht anstelle von T() in transform verwenden, da ich später den Standard-Konstruktor-Aufruf durch einen anderen ersetzen werde. Der Konstruktor von T bekommt einen int, der private deklariert und dem Aufrufer nicht bekannt ist.
    In etwa so:

    class A
    {
    public:
    	template <typename T>
    	std::string Convert(const std::wstring& w, const T& Dummy)
    	{
    		std::string Result;
    		std::transform(w.begin(), w.end(), std::back_inserter(Result), T(_TopSecret));
    		return Result;
    	}
    private:
    	int _TopSecret;
    };
    
    void foo()
    {
    	A a;
    	a.Convert(L"Hello World", StringConverter());
    }
    

    Bei der Methode mit dem Dummy-Parameter wird StringConverter allerdings 2 Mal erstellt.
    Nicht sehr effizient. Nicht sehr elegant.



  • Hallo,
    für Dummy-Parameter nimmt man in der Regel sowas wie:

    template <class T> struct Type2Type {};
    
    class A
    {
    public:
    template <class T> 
    void func(Type2Type<T> dummy);
    };
    
    int main()
    {
    A a;
    a.func(Type2Type<int>());
    }
    

    Alternativ kannst du auch Pointer im Zusammenhang mit dem Nullpointer verwenden. Mir persönlich gefällt die Type2Type-Variante aber besser.



  • Die Variante gefällt mir. 🙂
    Der Punkt der mangelnden Effizienz wäre damit erledigt.
    So elegant wie a.Func<Converter>() ist das zwar nicht, aber wenn der vc++ es eben anders nicht hinkriegt...


Anmelden zum Antworten