Frage

Das folgende Programm wird kompiliert:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage

int main()
{
    Test<&var> test;
}

Dies ist jedoch nicht der Fall, was für mich eine Überraschung ist:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
    Test<ptr> test; //FAIL! Expected constant expression.
}

Alternatives Beispiel:

int main()
{
   const int size = 42;
   int ok[*&size]; //OK

   const int * const pSize = &size;
   int fail[*pSize]; //FAIL
}

Ich bin zu dem Schluss gekommen, dass ein Zeiger kein konstanter Ausdruck sein kann, unabhängig davon, ob er const ist und mit einem konstanten Ausdruck initialisiert wurde.

Fragen:

  1. Ist meine Schlussfolgerung wahr?
  2. Wenn ja, warum kann ein Zeiger kein konstanter Ausdruck sein?Wenn nicht, warum werden die oben genannten Programme nicht kompiliert?
  3. Ändert C ++ 0x (C ++ 11, wenn Sie so wollen) etwas?

    Vielen Dank für Ihre Erkenntnisse!

War es hilfreich?

Lösung

Es ist etwas komplizierter. In C ++ 03 und C ++ 11 ist &var ein konstanter Ausdruck, wenn var eine lokale statische / Klassenstatik- oder Namespace-Bereichsvariable ist. Dies wird als Adresskonstantenausdruck bezeichnet. Das Initialisieren einer statischen Klassen- oder Namespace-Bereichszeigervariablen mit diesem konstanten Ausdruck wird garantiert durchgeführt, bevor Code ausgeführt wird (statische Initialisierungsphase), da es sich um einen konstanten Ausdruck handelt.

Nur seit C ++ 11 kann eine constexpr -Zeigervariable, in der der &var der Adresse gespeichert ist, auch als Adresskonstantenausdruck verwendet werden. Erst seit C ++ 11 können Sie eine Adresskonstante dereferenzieren Ausdruck (tatsächlich können Sie noch mehr dereferenzieren - sogar lokale Array-Elementadressen, aber lassen Sie es uns auf dem neuesten Stand halten). Wenn es sich um eine konstante integrale Variable handelt, die vor der Dereferenzierung initialisiert wurde, oder um eine constexpr-Variable, erhalten Sie erneut einen konstanten Ausdruck (abhängig von die Art und Wertkategorie, die Art des konstanten Ausdrucks kann variieren). Daher ist Folgendes in C ++ 11 gültig:

int const x = 42;
constexpr int const *px = &x;

// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }

Wenn ja, warum kann ein Zeiger kein konstanter Ausdruck sein? Wenn nicht, warum werden die oben genannten Programme nicht kompiliert?

Dies ist eine bekannte Einschränkung im Wortlaut des Standards. Derzeit sind nur andere Vorlagenparameter als Argumente oder & object für einen Vorlagenparameter vom Zeigertyp zulässig. Auch wenn der Compiler in der Lage sein sollte, viel mehr zu tun.

Andere Tipps

In C ++ 0x ist dies immer noch nicht zulässig. temp.arg.nontype erfordert:

Ein Vorlagenargument für einen Nicht-Typ-Nicht-Vorlagen-Vorlagenparameter muss eines der folgenden sein:

  • für einen Nicht-Typ-Vorlagenparameter vom Integral- oder Aufzählungstyp ein konvertierter konstanter Ausdruck (5.19) vom Typ des Vorlagenparameters; oder
  • der Name eines Nicht-Typ-Vorlagenparameters; oder
  • ein konstanter Ausdruck (5.19), der die Adresse eines Objekts mit statischer Speicherdauer und bezeichnet externe oder interne Verknüpfung oder eine Funktion mit externer oder interner Verknüpfung, einschließlich Funktionsvorlagen und Funktionsvorlagen-IDs, jedoch ohne nicht statische Klassenmitglieder, ausgedrückt (ohne Klammern) als & id-expression , außer dass das & weggelassen werden kann, wenn sich der Name auf eine Funktion oder ein Array bezieht und soll weggelassen werden, wenn der entsprechende Template-Parameter eine Referenz ist; oder
  • ein konstanter Ausdruck, der einen Nullzeigerwert (4.10) ergibt; oder
  • ein konstanter Ausdruck, der einen Nullelementzeigerwert (4.11) ergibt; oder
  • ein Zeiger auf ein Mitglied, ausgedrückt wie in 5.3.1 beschrieben.

    ursprüngliche Antwort:

    1. In C ++ 03 können nur integrale Ausdrücke konstante Ausdrücke sein.
    2. Weil der Standard dies (natürlich) sagt.
    3. In C ++ 0x enthält n3290 Beispiele für die Verwendung von constexpr für einen Zeiger. Was Sie also versuchen, sollte jetzt möglich sein, obwohl Sie jetzt das Schlüsselwort constexpr anstelle des const der obersten Ebene verwenden müssen.

      Es gibt auch einen gcc-Fehler, g ++ lehnt die eigenen Beispiele des Standardentwurfs für eine gültige Verwendung von constexpr ab.

Das Problem besteht darin, dass Ihr C ++ - Programm an jedem Punkt im Speicher geladen werden kann und die Adresse eines globalen vars daher bei jeder Ausführung des Programms unterschiedlich sein kann.Was passiert, wenn Sie Ihr Programm zweimal ausführen?var befindet sich dann offensichtlich an zwei verschiedenen Orten.

Schlimmer noch, in Ihrem Beispiel nehmen Sie die Adresse einer Variablen auf dem Stapel!Schau dir das an:

void myfunction( unsigned int depth) {
     const int myvar = depth;
     const int * const myptr = &myvar;
     if (depth)
         myfunction(depth-1);
}

Wenn main myfunction (3) aufruft, werden 3 myvars an verschiedenen Orten erstellt.Die Kompilierungszeit kann nicht einmal wissen, wie viele Myvars erstellt wurden, geschweige denn, wo genau sie sich befinden

Schließlich bedeutet das Deklarieren einer Variablen als const: "Ich verspreche es" und nicht bedeutet, dass dies eine Kompilierungszeitkonstante ist.Siehe dieses Beispiel:

int main(int argc, char** argv) {
    const int cargc = argc;
    char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top