2012-10-17 4 views
5

betrachten den CodeC++ Fehlerunterscheidungs ​​Spezialisierung wenn eine const Array vorbei

 template <class A> 
    class B; 

    template <class A> 
    class B<const A>{}; 

    template <class A, int N> 
    class B<A[N]>{}; 

    template <class A> 
    class B<A*>{}; 

    template <class A> 
    class B<A&>{}; 

die folgende Vorlage Instanziierungen funktionieren:

 A<int*&> 
    A<const int*> 
    A<int*[3]> 

aber das folgende nicht funktioniert:

 A<const int[3]> 

Gibt es einen Grund, dass diese bestimmte Kombination ungültig ist oder ist es vielleicht ein Fehler mit g ++ 4.6 .3?

Übrigens habe ich das mit SFINAE und boost :: disable_if <> umgehen können, so dass zumindest das Problem gelöst ist.

EDIT

habe ich vergessen, dass der Fehler in Frage zu erwähnen ist eine zweideutige Klasse Template-Instantiierung und es konnte nicht zwischen der Überlastung für const oder die Überlastung für eine Anordnung entscheiden.

EDIT2

Das hat nichts mit Zeigern zu tun hat, ist hier der volle Kontext:

Ich C++ Metaprogrammierung durch das Buch gehen und tue Frage 2-3 (Kapitel 2 Frage 3), die besagt:

Verwenden Sie die Typ Traits-Einrichtungen, um eine type_descriptor Klassenvorlage zu implementieren, deren Instanzen, wenn gestreamt, den Typ ihrer Vorlage Parameter: Hinweis: Wir können nicht verwenden RTTI mit dem gleichen Effekt, da nach 18.5.1 [lib.type.info], Absatz 7 des Standards, typeid (T) .name() nicht garantiert ein aussagekräftiges Ergebnis liefert.

Meine Lösung (einschließlich der Abhilfe für das Fehler compilation) ist wie folgt:

//QUESTION 2-3 
    template <class T, class enable = void> 
    struct type_descriptor 
    { 
     std::string operator()() const 
     { 
     return "Unknown"; 
     } 
    }; 

    //specializations for primitive types 
    #define TYPE_DESC_SPEC(type) template <> \ 
     struct type_descriptor<type,void>  \ 
     {std::string operator()() const{return #type;}}; 

    TYPE_DESC_SPEC(int) 
    TYPE_DESC_SPEC(long) 
    TYPE_DESC_SPEC(void) 
    TYPE_DESC_SPEC(short) 
    TYPE_DESC_SPEC(unsigned char) 
    TYPE_DESC_SPEC(unsigned short) 
    TYPE_DESC_SPEC(unsigned long) 

    //specializations for modifiers *, const, &, and [N] 

    template <class T> 
    struct type_descriptor<T&,void> 
    {std::string operator()(){return type_descriptor<T>()() + " &";}}; 

    template <class T> 
    struct type_descriptor<T*,void> 
    {std::string operator()(){return type_descriptor<T>()() + " *";}}; 

    //Replace void with what's in the comment for the workaround. 
    template <class T> 
    struct type_descriptor<const T, void/*typename boost::disable_if<boost::is_array<T> >::type*/> 
    {std::string operator()(){return type_descriptor<T>()() + " const";}}; 

    template <class T> 
    struct type_descriptor<T(*)(),void> 
    {std::string operator()(){return type_descriptor<T>()() + " (*)()";}}; 

    template <class T, class U> 
    struct type_descriptor<T(*)(U),void> 
    {std::string operator()(){return type_descriptor<T>()() + " (*)(" + type_descriptor<U>()() + ")";}}; 

    template <class T, int N> 
    struct type_descriptor<T[N],void> 
    { 
     std::string operator()() 
     { 
     std::stringstream s; 
     s << type_descriptor<T>()() << " [" << N << "]"; 
     return s.str(); 
     } 
    }; 

    template <class T> 
    struct type_descriptor<T[],void> 
    {std::string operator()(){return type_descriptor<T>()() + " []";}}; 

    //Now overload operator<< to allow streaming of this class directly 

    template <class T> 
    std::ostream & operator<<(std::ostream & s, type_descriptor<T> t) 
    { 
     return s << t(); 
    } 
    //END QUESTION 2-3 

Verwendungsbeispiel ist:

 std::cout << "\nQuestion 2-3 results\n"; 
     std::cout << type_descriptor<int*>() << std::endl; 
     std::cout << type_descriptor<int*[3]>() << std::endl; 
     std::cout << type_descriptor<std::string*>() << std::endl; 
     std::cout << type_descriptor<const int&>() << std::endl; 
     std::cout << type_descriptor<const int *const&>() << std::endl; 
     std::cout << type_descriptor<int[4]>() << std::endl; 
     std::cout << type_descriptor<int(*)()>() << std::endl; 
     std::cout << type_descriptor<int*&(*)(const char &)>() << std::endl; 
     std::cout << type_descriptor<int*&>() << std::endl; 
     std::cout << type_descriptor<int[]>() << std::endl; 
     std::cout << type_descriptor<const long[]>() << std::endl; 

und der entsprechende Ausgang ist (wenn die Umgehung in ist, andernfalls kompiliert es nicht auf der letzten):

 
int * 

int * [3] 

Unknown * 

int const & 

int const * const & 

int [4] 

int (*)() 

int * & (*)(Unknown const &) 

int * & 

int [] 

long const [] 

So C++ kann anders iate Zeiger und Arrays für die Template-Parameter, ist in der Lage, korrekt und rekursiv Compound-Typen zu trennen und das korrekte Ergebnis auszugeben, mit Ausnahme von const A[]. Dazu braucht es Hilfe

+0

Gehen Sie zu raten - aber ich denke 'const int [3]' zerfällt in 'const int *' –

+0

@AdrianCornish nein, das ist es nicht, siehe die Bearbeitung. Abgesehen davon, wenn das das dritte gültige Beispiel wäre, sollte ich auch nicht funktionieren. – SirGuy

+0

3. würde funktionieren, weil 'const int *' anders ist als 'int *' - versuchen (erfolglos), den Abschnitt des Standards zu finden, der den Zerfall dokumentiert ;-) –

Antwort

1

Ein Array-Typ mit einem const-Elementtyp ist sowohl ein const-qualifizierter Typ (const gilt bidirektional) als auch ein Array-Typ.

Also sollten Sie die Spezialisierungen beheben.

+0

Warum ist es so für Arrays und nicht für Zeiger oder Referenzen? – SirGuy

+0

, weil es für Zeiger nicht sinnvoll ist. Zeiger können const sein und auf const zeigen. und Referenzen, die eine Indirektion sind, könnten konzeptuell auch zwei verschiedene Constants haben, aber da eine Referenz nicht geändert werden kann, um auf verschiedene Dinge zu verweisen, ist die Const auf der Referenz selbst verboten. Auf der anderen Seite ist der Wert eines Arrays nicht mehr als alle seine Elemente, ohne Indirektion. es macht also Sinn, dass const bidirektional gilt. –

+0

Hat diese Definition einen Vorteil gegenüber der Annahme, dass 'const int [] 'ein Array von Konstanten ist? Ich meine in dem Sinne, dass die obigen Template-Spezialisierungen nicht mehrdeutig wären und die Array-Spezialisierung instanziiert wäre. – SirGuy