2016-04-12 9 views
4

Lassen Sie uns sagen, ich habe diese Klasse:Wie wird ein Konstanten-Array basierend auf einem Konstruktorargument aufgefüllt?

template<int N> 
class A { 
    public: 
    A(const char *s) ... 
    private: 
    const char buf[N]; 
}; 

Die Vorlage ist so, dass ich die Array-Größe ohne dynamische Speicherzuweisung konfigurieren kann (eine Anforderung). Das Element buf ist const, da es über die Lebensdauer des Objekts konstant bleiben soll, nachdem das Objekt initialisiert wurde.

Um zu klären, ich nicht auch Zugriff auf die STL haben.

Welche Optionen habe ich, um diesen Konstruktor zu definieren, damit ich den Inhalt von s in buf kopieren kann? Eine Option ist const_cast, aber ich suche nach Alternativen, die dies nicht erfordert.

+2

Können Sie 'char buf [N]' auf 'std :: ändern array'? – Jarod42

+0

@ Jarod42 Nein, leider. Ich habe keinen Zugang zu STL. – Ana

+0

@Ana können Sie die Quellen von 'libstdC++' oder etwas kopieren? Selbst wenn Sie keinen Zugriff auf 'std :: array' haben, werden Sie es jetzt neu implementieren. Wahrscheinlich schlechter als in der ursprünglichen Implementierung. –

Antwort

3

Sie verwenden eine index_sequence und Vorlage Expansion.

#include <utility> 
#include <cstdlib> 


template<int N> 
class A { 

    template<size_t...Is> 
    A(const char (&s)[N], std::index_sequence<Is...>) 
    : buf{ s[Is]... } 
    {} 


    public: 
    A(const char (&s)[N]) 
     : A(s, std::make_index_sequence<N>()) 
    { 
    } 

    private: 
    const char buf[N]; 
}; 


int main() 
{ 
    A<3> a("ab"); 

}; 

Und weil const char [] eine wörtliche Art ist, sondern ermöglicht auch die Klasse constexpr zu sein:

#include <utility> 
#include <cstdlib> 


template<int N> 
class A { 

    template<size_t...Is> 
    constexpr A(const char (&s)[N], std::index_sequence<Is...>) 
    : buf{ s[Is]... } 
    {} 


    public: 
    constexpr A(const char (&s)[N]) 
     : A(s, std::make_index_sequence<N>()) 
    { 
    } 

    private: 
    const char buf[N]; 
}; 


int main() 
{ 
    constexpr A<3> a("ab"); 

}; 

aber das ändert die Unterschrift ...

ok dann, das:

#include <utility> 
#include <cstdlib> 
#include <cstring> 


template<int N> 
class A { 

    template<size_t...Is> 
    constexpr A(const char *s, size_t len, std::index_sequence<Is...>) 
    : buf{ (Is <= len ? s[Is] : char(0))... } 
    {} 


    public: 
    constexpr A(const char *s) 
     : A(s, strlen(s), std::make_index_sequence<N>()) 
    { 
    } 

    private: 
    const char buf[N]; 
}; 


int main() 
{ 
    constexpr A<10> a("ab"); 

}; 
+2

Beachten Sie, dass Sie die Signatur ändern, um genau' N' zu erhalten. – Jarod42

+0

@ Jarod42 können wir das lösen ... sec –

+0

@RichardHodges: Ich habe das gelöst: P (siehe meine Lösung) – Nawaz

4

Die Lösung, die durch @ Richard Hodges erfordert die Klasse, die mit char Array initialisiert werden, wie zum char const* gegenüber, die ändert die Signatur des Konstruktors. Wenn das nicht für Ihren Fall nicht funktioniert, dann ist hier eine Lösung, die funktioniert nicht Änderung der Unterschrift:

template<int N> 
class A 
{ 
     template<size_t...Is> 
     A(const char * s, std::index_sequence<Is...>) 
     : _assert(std::strlen(s) <= N, "size of buffer is bigger than N"), 
      buf{ (Is, *s != '\0'? *s++: *s)... } 
     {} 

    public: 
     A(const char *s) : A(s, std::make_index_sequence<N>()) {} 

    private: 
     throwable _assert; 
     const char buf[N]; 
}; 

wo throwable ist wie folgt definiert:

struct throwable 
{ 
    throwable(bool b, char const * message){ 
     if (not b) 
      throw std::invalid_argument(message); 
    } 
}; 

Die Verwendung von throwable stellt sicher, dass buf wird nicht mit Puffer größer als N Bytes initialisiert. Wenn Ihre Situation jedoch sicherstellt, dass diese Überprüfung nicht erforderlich ist, können Sie sie entfernen. Der Code sollte auch ohne ihn funktionieren, aber ich würde vorschlagen, dass Sie ihn mindestens in debug Modus halten.

beachte, dass die Zugabe von _assert als Mitglied der Größe der Klasse, um ein Byte mindestens erhöht. Um dies zu vermeiden, könnten wir empty base class optimiation verwenden und tun dies statt:

template<int N> 
class A : private throwable 
{ 
     template<size_t...Is> 
     A(const char * s, std::index_sequence<Is...>) 
     : throwable(std::strlen(s) <= N, "size of buffer is bigger than N"), 
      buf{ (Is, *s != '\0'? *s++: *s)... } 
     {} 
    public: 
     A(const char *s) : A(s, std::make_index_sequence<N>()) {} 

    private: 
     const char buf[N]; 
}; 

Das spart uns 1 Byte pro Instanz.

+0

Hinweis: erfordert C++ 14 –

+0

@ M. M: Äquivalente von 'std :: make_index_sequence' und' std :: index_sequence' sind auch in C++ 11 implementierbar. – Nawaz

+1

Ich mag die Throwable, wenn Sie es einen Contexpr Konstruktor geben (und das gleiche mit A), dann können Sie die Variable Contexpr deklarieren - und dies wird Ihnen eine Kompilierungszeit Ausnahme mit Stack-Trace geben, wenn die String-Länge falsch ist. –

Verwandte Themen