Forwarding Problem mit C++11



  • Hiho,

    ich wollte mir eine kleine Exception-Template-Klasse zur Hilfe schreiben.

    template <typename T>
    class ExceptionBase
    {
    	public:
    		//
    		typedef T StringType;
    
    		//
    		template<typename U>
    		ExceptionBase(U&& error) : m_error(std::forward<U>(error)) { }
    
    		//ExceptionBase(const StringType& error) : m_error(error) { }
    
    		//
    		const StringType& getMessage() const { return m_error; }
    
    		//
    		StringType consumeMessage() { return StringType(std::move(m_error)); }
    
    	private:
    		//
    		StringType m_error;
    };
    
    //
    template <typename T,typename Distinct>
    class Exception : public ExceptionBase<T>
    {
    	public:
    		//
    		template<typename U>
    		Exception(U&& error) : ExceptionBase(std::forward<U>(error)) { }
    };
    

    Kurz zur Erklärung. Die Base-Klasse bekommt als Parameter den Stringtyp, z. B. std::string oder std::wstring). Wenn ich nur diese Klasse benutze, kann ich Exceptions von einem Stringtyp nicht mehr unterscheiden. Daher gibt es das Exception-Template mit dem 2. Templateparameter 'Distinct'. Dieser dient nur dazu, verschiedene Typen zu haben, um Exceptions unetrscheiden zu können.

    Den Parameter für den Konstruktor wollte ich nun ebenfalls variable machen, sodass const-Referenzen übergeben werden können oder const char*-Parameter, aber auch rvalue-References für die move-Semantik. Nun habe ich einfach das forwarding-Pattern übernommen. Bei der Exception-Klasse geht das auch, für die Exception-Base bekomme ich aber nun folgende Fehlermeldung:

    Instantiierung des Templates:

    typedef Exception<std::string,CTubeContours> CTubeConturException;
    throw CTubeConturException(CErrorMsg::stpReadError);
    

    wobei CErrorMsg::stpReadError ein const char* ist.
    Das VS 2010 wirft mir nun folgende Fehlermeldung

    1>f:\quellcode\cpp\zis\ziscascadefacade\ziscascadefacade\exceptionbase.h(16): error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const std::basic_string<_Elem,_Traits,_Ax> &)': Konvertierung des Parameters 1 von 'const Exception<T,Distinct>' in 'const std::basic_string<_Elem,_Traits,_Ax> &' nicht möglich
    1> with
    1> [
    1> _Elem=char,
    1> _Traits=std::char_traits<char>,
    1> _Ax=std::allocator<char>
    1> ]
    1> and
    1> [
    1> T=std::string,
    1> Distinct=CTubeContours
    1> ]
    1> and
    1> [
    1> _Elem=char,
    1> _Traits=std::char_traits<char>,
    1> _Ax=std::allocator<char>
    1> ]
    1> Ursache: Konvertierung von 'const Exception<T,Distinct>' in 'const std::basic_string<_Elem,_Traits,_Ax>' nicht möglich
    1> with
    1> [
    1> T=std::string,
    1> Distinct=CTubeContours
    1> ]
    1> and
    1> [
    1> _Elem=char,
    1> _Traits=std::char_traits<char>,
    1> _Ax=std::allocator<char>
    1> ]
    1> Kein benutzerdefinierter Konvertierungsoperator verfügbar, der diese Konvertierung durchführen kann, oder der Operator kann nicht aufgerufen werden
    1> f:\quellcode\cpp\zis\ziscascadefacade\ziscascadefacade\tubecontours.cpp(26): Siehe Verweis auf die Instanziierung der gerade kompilierten Funktions-template "ExceptionBase<T>::ExceptionBase<const Exception<T,Distinct>&>(U)".
    1> with
    1> [
    1> T=std::string,
    1> Distinct=CTubeContours,
    1> U=const Exceptionstd::string,CTubeContours &
    1> ]

    Ich verstehe nun nicht, wo mein Denkfehler liegt. Was mache ich falsch?

    VG

    Pellaeon



  • Nimm doch zur Typunterscheidung einfach ein non-template parameter.
    Dann kannst du Exception-IDs erstellen, wie bspw.

    namespace ExceptionID
    {
        enum class ExceptionID
        {
            CTubeContours,
            BlaBlah,
            ///.......
        }
    }
    

    Instanziierung:

    typedef Exception<std::string, ExceptionID::CTubeCountours> CTubeConturException;
    throw //....
    

    Außerdem verstehe ich überhaupt nicht, wieso du umbedingt einen generischen Stringtyp brauchst. Das ist IMHO total unnötig. Nimm einfach einen std::string .



  • Dein Fehler ist übrigens, dass du ExceptionBase den falschen Template-Parameter übergibst. Das da oben ist also nicht dein richtiger Code.

    Siehe Verweis auf die Instanziierung der gerade kompilierten Funktions-template "ExceptionBase<T>::ExceptionBase<const Exception<T,Distinct>&>(U)".



  • Außerdem verstehe ich überhaupt nicht, wieso du umbedingt einen generischen Stringtyp brauchst. Das ist IMHO total unnötig. Nimm einfach einen std::string.

    Weil ich ein bisschen mit den neuen C++11-Features rumspielen will, um sie zu lernen. Klar gehts auch ohne, aber um den Lerneffekt willen möchte ich einfach wissen, warum es nicht geht, wo mein Fehler liegt.

    VG

    Pellaeon



  • Sone schrieb:

    Dein Fehler ist übrigens, dass du ExceptionBase den falschen Template-Parameter übergibst. Das da oben ist also nicht dein richtiger Code.

    Siehe Verweis auf die Instanziierung der gerade kompilierten Funktions-template "ExceptionBase<T>::ExceptionBase<const Exception<T,Distinct>&>(U)".

    Die Fehlermeldung da habe ich auch nicht verstanden. Der Code oben ist 1:1 aus dem VS rauskopiert! Ich verstehe nichtm warum da dieser Typ landet und nicht mein const char*.



  • Probier mal in Zeile 33 ExceptionBase<T> , also die volle Template-ID statt nur dem Namen.
    Könnte helfen.

    Was auch noch eine Möglichkeit ist: VC++-Bug... glaube ich aber weniger...



  • Sone schrieb:

    Probier mal in Zeile 33 ExceptionBase<T> , also die volle Template-ID statt nur dem Namen.
    Könnte helfen.

    Guter Hinweise 🙂 Hat aber leider nicht geholfen 😕

    Sone schrieb:

    Was auch noch eine Möglichkeit ist: VC++-Bug... glaube ich aber weniger...

    Unwahrscheinlich, aber nicht unmöglich. Ich werde das ganze mal im VS2012 noch testen.

    Aber vom Code her oben, ist der logisch so richtig, oder ist das forwarding ans forwarding an den String-Konstruktor irgendwo falsch?



  • VC++ 2012:

    ========== Erstellen: 1 erfolgreich, 0 fehlerhaft, 0 aktuell, 0 übersprungen ==========



  • Pellaeon schrieb:

    Aber vom Code her oben, ist der logisch so richtig, oder ist das forwarding ans forwarding an den String-Konstruktor irgendwo falsch?

    Ich kenne mich nicht sehr gut aus mit Perfect Forwarding in C++11. Aber das sieht völlig in Ordnung aus.

    Bei mir funktioniert auf dem GCC 4.8 dieser Code Fehlerfrei, auf dem GCC 4.5.1 in Ideone leider nicht. Komisch.



  • Tja! Das ist wohl einfach ein Bug in einer früheren Version. Mein GCC 4.8 kompiliert einen Test-Code mit obigen Klassen fehlerfrei, und dein VC++ 2012 auch...



  • Jo sieht so aus.
    Danke für deine Hilfe! 🙂


Log in to reply