Stringkosntante: Länge zur Compile-Zeit



  • Ich habe eine Klasse:

    class str
    {
       char const * beg; 
       char const * end;   
    
    public:
       str(char const * p) : beg(p), end(p+strlen(p)) {}
    };
    

    gibt es eine Möglichkeit, sich das strlen zur Laufzeit zu sparen, wenn str mit einer Stringkonstante initialisiert wird?


  • Mod

    der typ eines stringliterals ist array aus konstatem char/wchar_t. folglich läßt sich die länge des strings aus der größe des arrays ermitteln.

    class str
    {
       char const * beg;
       char const * end;  
    
    public:
        template<std::size_t N>
        str(const char(&str)[N]) : beg(str), end(str+N-1) {}
    };
    


  • Wenn ich beide Konstruktoren angebe, nimmt mein compiler immer den str(TCHAR const 😉 - und den brauch' ich für nicht-Konstanten. Gibt's da noch 'ne trick?

    Ich könnte das aber über eine Hilfsklasse/Funktion nutzen - reicht ja auch 🙂



  • Also an und für sich sollte das schon so gehen. Wichtig ist nur, dass die Variable auch ein Array ist sonst funktioniert das nicht.

    Angenommen deine Konstruktoren sehen so aus:

    // Konstruktor 1
        str(char const * s): beg(s), end(s + strlen(s)) {}
    
        // Konstruktor 2
        template<std::size_t N>
        str(const char(&sa)[N]) : beg(sa), end(sa+N-1) {}
    
    char const * foo = "BlubberBlaFoo"; 
    
    str foo_str(foo); //Konstruktor 1
    
    char bar[] = "Sonstwas";
    
    str bar_str(bar); // Konstruktor 2
    

    Wenn das zur Kompilierzeit bestimmt werden soll, muss es ein Array sein. Aus einem char const* kann die Länge nicht zur Kompilierzeit bestimmt werden.

    BR
    Vinzenz


  • Mod

    mach aus dem anderen konstruktor auch ein template, wobei ungültige typen per SFINAE unterdrückt werden:

    #include <boost/utility/enable_if.hpp>
    #include <boost/type_traits.hpp>
    class str
    {
       char const * beg;
       char const * end;  
    
    public:
        template<typename T>
        str(T p, typename boost::enable_if_c<
                boost::is_same< T, const char* >::value || boost::is_same< T, char* >::value
            >::type* = 0)
            : beg(p), end(p+strlen(p)) {}
        template<std::size_t N>
        str(const char(&str)[N]) : beg(str), end(str+N-1) {}
    };
    


  • Für str("xyz");

    nimmt MSVC8 Konstruktor 1.

    Mit:

    template<size_t len> inline 
    str strc(const char (&s)[len])  
    { 
       return str(s,s+len-1);  
    }
    

    geht auch strc("xyz") - das reicht mir schon.


  • Mod

    evilissimo schrieb:

    // Konstruktor 1
        str(char const * s): beg(s), end(s + strlen(s)) {}
    
        // Konstruktor 2
        template<std::size_t N>
        str(const char(&sa)[N]) : beg(sa), end(sa+N-1) {}
    

    konstruktor 1 benötigt hier nur eine array-zu-pointer-konvertierung für array argumente und ist folglich ein "exact match" und damit immer besser als jede funktionstemplatespezialisierung. konstruktor 2 kann hier nie durch argument deduktion aufgerufen werden. indem der erste konstruktor in einen template konstruktor umgewandelt wird, wird diese problematik umgangen: einerseits werden für argumente, für die der parametertyp durch deduktion ermittelt werden, keine implizite konvertierungssequenzen angewandt und zweitens wäre dieser konstruktor auch nicht mehr automatisch dem anderen vorzuziehen, selbst wenn eine passende spezialisierung existieren würde.


Anmelden zum Antworten