2015-01-22 4 views
10

In dem folgenden C++ 11-Code verursacht der letzte Aufruf von ArraySize einen Kompilierungsfehler. Offenbar liegt dies daran, dass y ein Array mit der Größe einer Laufzeit ist und der ArraySize-Vorlagenparameter N nicht für y abgeleitet werden kann. Ich verstehe nicht, warum x ein kompilierzeitgroßes Array ist, aber y endet Laufzeitgröße. Die Arraysize Template-Funktion direkt von Scott Meyers "Effective Moderne C++" Item 1.C++: Warum ist das constexpr keine Kompilierzeitkonstante

#include <cstddef> 

template<typename T, std::size_t N> 
constexpr std::size_t arraySize(T(&)[N]) noexcept { return N; } 

struct S 
{ 
    char c[10]; 
}; 

int main() 
{ 
    S s; 
    S* ps = &s; 

    char x[arraySize(s.c)]; 
    char y[arraySize(ps->c)]; // why is y a runtime sized array? 

    arraySize(x); 
    arraySize(y); // error !? 

    return 0; 
} 
+0

Was ist der Fehler? –

+0

Nun, ich bin ratlos ... sowohl "sc" als auch "ps-" c "haben den gleichen Typ, wie gespuckt von' typeid (sc) .name() 'oder' typeid (ps-> c) .name() ', dh'A10_c' für g ++ – vsoftco

+0

Um es noch etwas dunkler zu machen: Wenn Sie' arraySize() 's Parameter einen Verweis auf const setzen, kompiliert das folgende:' char y [arraySize (declltype (ps-> c) {})]; ' – Quentin

Antwort

9

In C++ genommen wird, ist der Fehler nicht der Aufruf an arraySize(y), aber die Erklärung von y selbst.

Die Grenzen in einer Array-Deklaration müssen ein "konvertierter konstanter Ausdruck" sein.

Wenn Ihr Compiler die Deklaration y akzeptiert und Ihnen später sagt, dass y ein Array von Laufzeit gebunden ist, ist es kein C++ - Compiler. In keiner ratifizierten Version von C++ sind noch Arrays der Laufzeit gebunden, auch nicht der aktuelle Entwurf.

Der signifikante Unterschied zwischen arraySize(s.c) und arraySize(ps->c) ist, dass das gleiche wie ps->c(*ps).c ist und der * Dereferenzierungsoperator erfordert lvalue-zu-R-Wert-Umwandlung auf ps, die nicht ein konstanter Ausdruck ist (noch ist &s, siehe unten). Der Rest des Ausdrucks enthält keine Lvalue-to-Rvalue-Konvertierung, der Array-Lvalue ist direkt an die Referenz gebunden.

ein konstanter Ausdruck ist entweder ein glvalue Kern konstanter Ausdruck, dessen Wert bezieht sich auf eine Einheit, die eine zulässige Folge eines konstanten Ausdrucks ist (wie unten definiert) oder ein prvalue konstanter Kern Ausdruck, dessen Wert ein Objekt, in dem, für das Objekt und seine Subobjekte:

  • jedes nicht-statisches Datenelement des Referenztypen bezieht sich auf eine Einheit, die eine zulässige Folge eines konstanten Ausdrucks ist, und

  • i Wenn das Objekt oder Unterobjekt vom Zeigertyp ist, enthält es die Adresse eines Objekts mit statischer Speicherdauer, die Adresse nach dem Ende eines solchen Objekts (5.7), die Adresse einer Funktion oder einen Nullzeigerwert.

Eine Entität ist ein erlaubtes Ergebnis eines konstanten Ausdrucks, wenn sie ein Objekt mit statischer Speicherdauer ist, die entweder keine temporäres Objekt ist oder ein zeitweiliges Objekt, dessen Wertes satis fi es die oben genannten Einschränkungen, oder es ist ein Funktion.

Offensichtlich ps enthält die Adresse eines Objekts mit automatischer Speicherdauer, so kann es nicht constexpr deklariert werden. Aber alles sollte anfangen zu arbeiten, wenn Sie S s; S* ps = &s;-static S s; constexpr S* ps = &s;

(Auf der anderen Seite ändern, würden Sie denken, dass der Parameter auf arraySize(s.c) ist kein konstanter Ausdruck entweder, da es sich um eine Referenz ist und nicht zu einem Objekt der statischen Speicher Dauer)

+0

"Wenn Ihr Compiler akzeptiert die Deklaration von y und später sagt Ihnen, dass y ist ein Array von Laufzeit gebunden, es ist kein C++ - Compiler." - Dieser Teil ist nicht wahr, eine konforme Implementierung (Compiler) wird ausdrücklich erlaubt, schlecht geformte Eingaben auf dem Weg von Erweiterungen zu "akzeptieren" (d. H. Kompilieren und/oder ausführen), siehe z.B. 1.4/8 im N4296 Entwurf. Die Implementierung ** ist ** erforderlich, um in diesem Fall eine Diagnose auszustellen, und GCC und Clang geben keine Warnung für Arrays mit variabler Länge aus, es sei denn, die Option -pedantic wird dem Aufruf hinzugefügt. –