1. Kurze Antwort: Es funktioniert unabhängig davon constexpr
erklärt zu werden, weil Sie ein Objekt sind definiert, mit Statische Speicherdauer (das ist kein String-Literal - es speichert eine Kopie des Inhalts von eins) und seine Adresse ist ein konstanter Ausdruck. In Bezug auf die Verknüpfung hat str2
interne Verknüpfung, aber das ist in Ordnung - seine Adresse kann als ein nicht-Vorlage-Argument verwendet werden.
Lange Antwort:
In C++ 11 und 14 [14.3.2p1] sagt der folgende:
A template-argument for a non-type, non-template template-parameter shall be one of:
[...]
- a constant expression (5.19) that designates the address of a complete object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as
&
id-expression, where the id-expression is the name of an object or function, except that the &
may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference;
[...]
So können Sie die Adresse eines Objekts mit statischer Speicherdauer verwenden, aber das Objekt muss durch einen Namen mit einer Verknüpfung (intern oder extern) identifiziert werden, und die Art, wie Sie diese Adresse ausdrücken, ist eingeschränkt. (Zeichenfolgenliterale sind keine Namen und haben keine Verknüpfung.)
Kurz gesagt, funktioniert sogar char str1[] = "Test 1";
. static char str1[] = "Test 1";
ist auch in Ordnung; GCC 5.1.0 lehnt dies ab, aber ich denke, das ist ein Bug; Clang 3.6.0 akzeptiert es.
Über str2
‚s-Bindung, C++ 11 und 14 [3.5p3] sagt:
A name having namespace scope (3.3.6) has internal linkage if it is the name of
[...]
- a non-volatile variable that is explicitly declared
const
or constexpr
and neither explicitly declared extern
nor previously declared to have external linkage;
[...]
N4431 hat, dass etwas geändert, als Folge der DR 1686, zu:
- a variable of non-volatile const-qualified type that is neither explicitly declared
extern
nor previously declared to have external linkage;
widerspiegelt die Tatsache, dass constexpr
const-Qualifikation für Objekte bedeutet.
2. Kurze Antwort: Für C++ 11 und 14, siehe oben; für Entwurf C++ 1z, str3
ist kein konstanter Ausdruck, da der Zeiger selbst nicht constexpr
ist, und es ist auch die Adresse eines String-Literals. str4
ist konstant, aber immer noch eine Adresse eines String-Literals.
Lange Antwort:
Im aktuellen Arbeitsentwurf, N4431, die Beschränkungen für Nicht-Typ-Vorlage Argumente wurden gelockert. [14.3.2p1] sagt jetzt:
A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
- a subobject (1.8),
- a temporary object (12.2),
- a string literal (2.13.5),
- the result of a
typeid
expression (5.2.8), or
- a predefined
__func__
variable (8.4.1).
Und das sind alle Einschränkungen. Der konvertierte konstante Ausdruck Teil ist ziemlich wichtig; Die vollständige Definition ist lang, aber ein Teil relevant für unseren Fall ist, dass die Adresse eines Objekts mit statischer Speicherdauer ein solcher Ausdruck ist.
Von Bedeutung ist auch, dass nach [5.20p2.7], ein L-Wert-zu-R-Wert-Umwandlungs angewendet
a non-volatile glvalue that refers to a non-volatile object defined with constexpr
, or that refers to a non-mutable sub-object of such an object
auch die Voraussetzungen für ein konstanter Ausdruck ist. Dies ermöglicht uns, einige constexpr
Zeigervariablen als nicht typisierte Vorlagenargumente zu verwenden. (Beachten Sie, dass die einfache Deklaration einer Variablen const
nicht ausreicht, da sie mit einem nicht konstanten Ausdruck initialisiert werden kann.)
So etwas wie constexpr const char* str3 = str1;
ist in Ordnung. Es wird von Clang 3.6.0 im C++ 1z-Modus akzeptiert (und im C++ 14-Modus abgelehnt); GCC 5.1.0 lehnt es immer noch ab - es sieht so aus, als hätte es die aktualisierten Regeln noch nicht implementiert.
Dennoch, was ist falsch mit String-Literalen? Hier ist das Problem (N4431 [2.13.5p16]):
Evaluating a string-literal results in a string literal object with static storage duration, initialized from the given characters as specified above. Whether all string literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.
Eine Implementierung erlaubt ist, viele Dinge mit Stringliterale zu tun: mix, match, machen sie sich überlappen (ganz oder teilweise), machen 7 Kopien von der gleiche Übersetzungseinheit - was auch immer. Das macht die Adresse eines Zeichenfolgenliterals als ein nicht typisiertes Vorlagenargument unbrauchbar.
Ich habe vor kurzem eine ähnliche Frage, die gefragt, warum eine 'extern' -Deklaration nicht für die Kompilierung Zeit Wert Abzug verwendet werden kann. Es ist wahrscheinlich, weil der Linker tatsächlich "extern" behandelt, und der Compiler kann diese Information nicht verwenden, wenn der Code kompiliert wird. –
@ πάνταῥεῖ Was seltsam ist, ist, dass ich in (1) eine 'extern'-Definition verwende, die funktioniert. – vsoftco
Was '(3)' von den anderen beiden unterscheidet, ist, dass Sie möchten, dass die Adresse "innerhalb" der Variablen enthalten ist und nicht die Adresse * von * der Variablen selbst. Ich bin gerade an meinem Telefon und kann diese Frage zu einem späteren Zeitpunkt noch einmal beantworten, um eine Antwort zu geben. –