Ich möchte ein static const
char
Array in meiner Klasse haben. GCC beschwerte sich und sagte mir, ich solle es verwenden constexpr
, obwohl es mir jetzt sagt, dass es eine undefinierte Referenz ist. Wenn ich das Array zu einem Nichtmitglied mache, wird es kompiliert. Was ist los?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
c++
c++11
static-members
constexpr
Pubby
quelle
quelle
int
@MooingDuck kompiliert. Es funktioniert gut als Nichtmitglied. Würde das nicht auch gegen die Regel verstoßen?int
s betrügen. Als Nichtmitglied sollte dies nicht erlaubt sein, es sei denn, die Regeln für C ++ 11 wurden geändert (möglich)Antworten:
Fügen Sie Ihrer CPP-Datei hinzu:
Grund: Sie müssen die Definition des statischen Elements sowie die Deklaration angeben. Die Deklaration und der Initialisierer befinden sich in der Klassendefinition, die Elementdefinition muss jedoch getrennt sein.
quelle
decltype(foo::baz) constexpr foo::baz;
C ++ 17 führt Inline-Variablen ein
C ++ 17 behebt dieses Problem für
constexpr static
Mitgliedsvariablen, die eine Out-of-Line-Definition erfordern, wenn sie odr-verwendet wurden. In der zweiten Hälfte dieser Antwort finden Sie Details vor C ++ 17.Vorschlag P0386 Inline-Variablen bietet die Möglichkeit, den
inline
Spezifizierer auf Variablen anzuwenden . Insbesondere in diesem Fallconstexpr
impliziertinline
für statische Elementvariablen. Der Vorschlag lautet:und modifiziert [basic.def] p2:
und füge [depl.static_constexpr] hinzu :
C ++ 14 und früher
In C ++ 03 durften wir nur klasseninterne Initialisierer für const-Integrale oder const-Aufzählungstypen bereitstellen. In C ++ 11 wurde
constexpr
diese Verwendung auf Literal-Typen erweitert .In C ++ 11 müssen wir keine Namespace-Bereichsdefinition für ein statisches
constexpr
Mitglied angeben , wenn es nicht von odr verwendet wird. Dies können Sie dem Entwurf des C ++ 11-Standardabschnitts9.4.2
[class.static.data] entnehmen, in dem es heißt ( Hervorhebung meiner in Zukunft ):Dann stellt sich die Frage,
baz
ob hier etwas verwendet wird :Die Antwort lautet " Ja" . Daher benötigen wir auch eine Definition des Namespace-Bereichs.
Wie bestimmen wir also, ob eine Variable odr-verwendet wird ? Der ursprüngliche C ++ 11-Wortlaut in Abschnitt
3.2
[basic.def.odr] lautet:So
baz
hat sich eine Ausbeute konstanten Ausdruck aber die L - Wert-zu-R - Wert - Umwandlung nicht sofort angewendet , da es nicht anwendbar ist auf Grundbaz
ist ein Array. Dies wird in Abschnitt4.1
[conv.lval] behandelt, der besagt:Was in der angelegten Array-zu-Zeiger Umwandlung .
Dieser Wortlaut von [basic.def.odr] wurde aufgrund des Fehlerberichts 712 geändert, da einige Fälle nicht von diesem Wortlaut abgedeckt wurden, diese Änderungen jedoch die Ergebnisse für diesen Fall nicht ändern.
quelle
constexpr
das absolut nichts damit zu tun hat? (baz
ist sowieso ein konstanter Ausdruck)integral or enumeration type
aber ansonsten ist es wichtig, dass es ein konstanter Ausdruck ist .Dies ist wirklich ein Fehler in C ++ 11 - wie andere erklärt haben, hat in C ++ 11 eine statische constexpr-Mitgliedsvariable im Gegensatz zu jeder anderen Art von constexpr-globaler Variable eine externe Verknüpfung und muss daher irgendwo explizit definiert werden.
Es ist auch erwähnenswert, dass Sie in der Praxis beim Kompilieren mit Optimierung häufig mit statischen constexpr-Mitgliedsvariablen ohne Definitionen davonkommen können, da diese bei allen Verwendungszwecken inline sein können. Wenn Sie jedoch ohne Optimierung kompilieren, kann Ihr Programm häufig keine Verknüpfung herstellen. Dies macht dies zu einer sehr häufigen versteckten Falle - Ihr Programm lässt sich gut mit der Optimierung kompilieren, aber sobald Sie die Optimierung deaktivieren (möglicherweise zum Debuggen), kann keine Verknüpfung hergestellt werden.
Gute Nachrichten - dieser Fehler wurde in C ++ 17 behoben! Der Ansatz ist allerdings etwas kompliziert: In C ++ 17 sind statische constexpr-Mitgliedsvariablen implizit inline . Die Inline-Anwendung auf Variablen ist ein neues Konzept in C ++ 17, bedeutet jedoch effektiv, dass sie nirgendwo eine explizite Definition benötigen.
quelle
Ist die elegantere Lösung nicht das Ändern
char[]
in:Auf diese Weise können wir die Definition / Deklaration / Initialisierer in einer Codezeile haben.
quelle
char[]
Siesizeof
die Länge des Strings zur Kompilierungszeit abrufen, mit könnenchar *
Sie nicht (es wird die Breite des Zeigertyps zurückgegeben, in diesem Fall 1).sizeof
Problem nicht aufweist und in "Nur-Header" -Lösungen verwendet werden kannMeine Problemumgehung für die externe Verknüpfung statischer
constexpr
Elemente besteht darin, Referenzelement-Getter zu verwenden (was nicht auf das Problem @gnzlbg stößt, das als Kommentar zur Antwort von @deddebme ausgelöst wurde).Diese Redewendung ist mir wichtig, weil ich es hasse, mehrere CPP-Dateien in meinen Projekten zu haben, und versuche, die Anzahl auf eins zu beschränken, die nur aus
#include
s und einermain()
Funktion besteht.quelle
In meiner Umgebung ist gcc vesion 5.4.0. Durch Hinzufügen von "-O2" kann dieser Kompilierungsfehler behoben werden. Es scheint, dass gcc diesen Fall behandeln kann, wenn es um Optimierung geht.
quelle