Template: Vermeidung partieller Spezialisierung bei char/wchar_t



  • alter Falter! Respekt! Danke noch mal!

    ein kleiner Fehler ist noch in der Klasse:

    typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr; }
    soll vermutlich heißen:
    typename boost::disable_if<boost::is_convertible<const wchar*, T>, T>::type convert(T*) const { return wstr; }
    

  • Mod

    muffmolch schrieb:

    ein kleiner Fehler ist noch in der Klasse:

    typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr; }
    soll vermutlich heißen:
    typename boost::disable_if<boost::is_convertible<const wchar*, T>, T>::type convert(T*) const { return wstr; }
    

    Das ist schon so beabsichtigt. Es wird absichtlich nur auf Konvertierbarkeit in char* geprüft. Auf diese Weise vermeiden wir Probleme falls ein String zufällig mit beiden initialisiert werden kann - in diesem Falle wird char bevorzugt. Beachte, dass es einmal enable_if, und dann disable_if heißt. Man kann das nat. auch anders machen, z.B. im Falle von Mehrdeutigkeit ganz bewusst einen Fehler produzieren.


  • Mod

    Wenn Geschwindigkeit gefragt ist, kann man das noch ein bisschen verbessern, sofern die Strings alle auch mit Iteratoren initialisiert werden können):

    struct MultiLiteral
    {
        template <std::size_t N>
        /*constexpr*/ MultiLiteral(const char (&str)[N], const wchar_t (&wstr)[N]) : str(str), wstr(wstr), n(N) {}
        template <typename T>
        /*constexpr*/ operator T() const { return convert((T*)0); }
        template <typename T>
        /*constexpr*/ typename boost::enable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return str(str, str + n); }
        template <typename T>
        /*constexpr*/ typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr(wstr, wstr + n); }
        const char* str;
        const wchar_t* wstr;
        std::size_t n;
    };
    

    So kann sich die Stringklasse den Aufruf von (w)strlen sparen.



  • ok! werd ich gleich mal testen. Momentan stolpere ich darüber, dass ich bei boost::list_of ambiguous calls erhalte...

    struct A {
    	A( const char * ) {}
    	A( const wchar_t * ) {}
    
    	A& operator=( A & )		 { return *this; }
    	A& operator=( const char* t    ) { return *this; }
    	A& operator=( const wchar_t* t ) { return *this; }
    };
    
    int main( int, char * )
    {
      A a = ML("ZESZ");  //funktioniert
      vector< A > ddd1 = boost::assign::list_of(ML("A"))(ML("B")); //ambiguous
                //main.cpp: could be 'A &A::operator =(const wchar_t *)'
                //main.cpp: or       'A &A::operator =(const char *)'
    }
    

  • Mod

    A& operator=( [b][i]const[/i][/b] A & )
    

    Ach ja, im Falle von Mehrdeutigkeit (char/wchar_t) hilft das enable_if doch nicht weiter - denn der Compiler weiß trotzdem nicht, welchen Konvertierungsoperator er benutzen muss.
    In diesem Falle bleibt wahrscheinlich doch nur, einen extra Konvertierungsoperator für jede einzelne Stringklasse zu schreiben.



  • genau... hab ich auch grad festgestellt. const A& ist natürlich im Code so (war ja nur ein dummy Bsp).


  • Mod

    Wenn die Stringklassen alle Templateklassen sind (und ansonsten nicht noch zusätzliche Konvertierungen haben, die stören könnten), könnte man den Konvertierungsoperator entsprechend einschränken:

    #include <iostream>
    #include <boost/type_traits.hpp>
    #include <boost/utility.hpp>
    #include <boost/assign.hpp>
    
    struct MultiLiteral
    {
        constexpr MultiLiteral(const char* str, const wchar_t* wstr) : str(str), wstr(wstr) {}
        template <template <typename...> class String, typename... Args>
        operator String<Args...>() const { return convert((String<Args...>*)0); }
        template <typename T>
        typename boost::enable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return str; }
        template <typename T>
        typename boost::disable_if<boost::is_convertible<const char*, T>, T>::type convert(T*) const { return wstr; }
        const char* str;
        const wchar_t* wstr;
    };
    #define ML( x ) MultiLiteral( x, L##x )
    
    template <typename T>
    struct A {
        A( const char * ) {}
        A( const wchar_t * ) {}
    
        A& operator=( const A & )        { return *this; }
        A& operator=( const char* t    ) { return *this; }
        A& operator=( const wchar_t* t ) { return *this; }
    };
    
    #include <vector>
    using namespace std;
    
    int main()
    {
      A<int> a = ML("ZESZ");
      A<int> b( ML("ZESZ") );
      b = ML("ZESZ");
      vector< A<int> > ddd1 = boost::assign::list_of(ML("A"))(ML("B")); //ambiguous
                //main.cpp: could be 'A &A::operator =(const wchar_t *)'
                //main.cpp: or       'A &A::operator =(const char *)'
    }
    

    Läuft mit g++ 4.5.2.
    Wenn der Compiler noch keine variadic Templates kennt, müsste man wiederum die Parameter konkreter spezifizieren.



  • nein, das schluckt mein Compiler nicht (VS2008).

    Und bei der anderen Klasse erhalte ich einen Linkerfehler bei:

    string s = ML("test"); 
    //unresolved external symbol "public: __thiscall CMultiliteral::operator<char const *> char const *(void)const " 
    //unresolved external symbol "public: __thiscall CMultiliteral::operator<wchar_t const *> wchar_t const *(void)const " 
    
    string s= (const char*)ML("test"); //-funktioniert
    

  • Mod

    Vollständigen Code bitte.



  • Mit der Klasse aus deinem Beitrag vom 13:28:22 29.06.2011. Allerdings funktioniert es momentan.. Der Grund für den Linkerfehler ist mir nicht so ersichtlich. Wird wohl einmal mehr ein VS Problem gewesen sein, denn nach einem Neustart des Rechners wurde derselbe Code anstandslos übersetzt.. In dieseem Momenten wünsche ich mit den gcc oder icc zurück!


Anmelden zum Antworten