statische Variablen in einer Inline-Funktion

83

Ich habe eine Funktion, die in einer Header-Datei deklariert und definiert ist. Dies ist ein Problem für sich. Wenn diese Funktion nicht inline ist, erhält jede Übersetzungseinheit, die diesen Header verwendet, eine Kopie der Funktion, und wenn sie miteinander verknüpft sind, werden sie dupliziert. Ich habe das "behoben", indem ich die Funktion inline gemacht habe, aber ich befürchte, dass dies eine fragile Lösung ist, da der Compiler meines Wissens kein Inlining garantiert, selbst wenn Sie das Schlüsselwort "inline" angeben. Wenn dies nicht der Fall ist, korrigieren Sie mich bitte.

Die eigentliche Frage ist jedoch, was mit statischen Variablen innerhalb dieser Funktion passiert. Wie viele Exemplare habe ich am Ende?

Dirk Groeneveld
quelle

Antworten:

104

Ich denke, dir fehlt hier etwas.

statische Funktion?

Wenn Sie eine Funktion als statisch deklarieren, wird sie in ihrer Kompilierungseinheit "versteckt".

Ein Name mit Namespace-Bereich (3.3.6) hat eine interne Verknüpfung, wenn es sich um den Namen von handelt

- eine Variable, Funktion oder Funktionsvorlage, die explizit als statisch deklariert ist;

3,5 / 3 - C ++ 14 (n3797)

Wenn ein Name eine interne Verknüpfung hat, kann auf die Entität, die er bezeichnet, durch Namen aus anderen Bereichen in derselben Übersetzungseinheit verwiesen werden.

3,5 / 2 - C ++ 14 (n3797)

Wenn Sie diese statische Funktion in einem Header deklarieren, verfügen alle Kompilierungseinheiten einschließlich dieses Headers über eine eigene Kopie der Funktion.

Die Sache ist, wenn diese Funktion statische Variablen enthält, hat jede Kompilierungseinheit, die diesen Header enthält, auch eine eigene, persönliche Version.

Inline-Funktion?

Wenn Sie es als Inline deklarieren, ist es ein Kandidat für Inlining (dies bedeutet heutzutage in C ++ nicht viel, da der Compiler inline ist oder nicht, wobei manchmal die Tatsache ignoriert wird, dass das Schlüsselwort inline vorhanden ist oder fehlt):

Eine Funktionsdeklaration (8.3.5, 9.3, 11.3) mit einem Inline-Spezifizierer deklariert eine Inline-Funktion. Der Inline-Spezifizierer gibt der Implementierung an, dass die Inline-Substitution des Funktionskörpers am Aufrufpunkt dem üblichen Funktionsaufrufmechanismus vorzuziehen ist. Eine Implementierung ist nicht erforderlich, um diese Inline-Ersetzung am Aufrufpunkt durchzuführen. Selbst wenn diese Inline-Ersetzung weggelassen wird, bleiben die anderen in 7.1.2 definierten Regeln für Inline-Funktionen ein.

7.1.2 / 2 - C ++ 14 (n3797)

In einem Header hat dies einen interessanten Nebeneffekt: Die Inlined-Funktion kann im selben Modul mehrmals definiert werden, und der Linker fügt "sie" einfach zu einem zusammen (wenn sie aus Compiler-Gründen nicht inliniert wurden).

Für statische Variablen, die im Inneren deklariert sind, gibt der Standard ausdrücklich eine und nur eine davon an:

Eine statische lokale Variable in einer externen Inline-Funktion bezieht sich immer auf dasselbe Objekt.

7.1.2 / 4 - C ++ 98 / C ++ 14 (n3797)

(Funktionen sind standardmäßig extern. Wenn Sie Ihre Funktion nicht ausdrücklich als statisch markieren, gilt dies für diese Funktion.)

Dies hat den Vorteil "statisch" (dh es kann in einem Header definiert werden) ohne seine Fehler (es existiert höchstens einmal, wenn es nicht inline ist)

statische lokale Variable?

Statische lokale Variablen haben keine Verknüpfung (sie können außerhalb ihres Gültigkeitsbereichs nicht mit Namen bezeichnet werden), haben jedoch eine statische Speicherdauer (dh sie sind global, aber ihre Konstruktion und Zerstörung unterliegen bestimmten Regeln).

statisch + inline?

Das Mischen von Inline und Static hat dann die von Ihnen beschriebenen Konsequenzen (selbst wenn die Funktion inline ist, ist die darin enthaltene statische Variable nicht vorhanden, und Sie werden mit so vielen statischen Variablen enden, wie Sie über Kompilierungseinheiten verfügen, einschließlich der Definition Ihrer statischen Funktionen ).

Antwort auf die zusätzliche Frage des Autors

Seit ich die Frage geschrieben habe, habe ich sie mit Visual Studio 2008 ausprobiert. Ich habe versucht, alle Optionen zu aktivieren, mit denen VS in Übereinstimmung mit Standards funktioniert, aber es ist möglich, dass ich einige verpasst habe. Dies sind die Ergebnisse:

Wenn die Funktion nur "inline" ist, gibt es nur eine Kopie der statischen Variablen.

Wenn die Funktion "statisch inline" ist, gibt es so viele Kopien wie Übersetzungseinheiten.

Die eigentliche Frage ist nun, ob die Dinge so sein sollen oder ob dies eine Eigenart des Microsoft C ++ - Compilers ist.

Ich nehme an, Sie haben so etwas:

void doSomething()
{
   static int value ;
}

Sie müssen sich darüber im Klaren sein, dass die statische Variable innerhalb der Funktion, einfach ausgedrückt, eine globale Variable ist, die für alle außer dem Funktionsumfang verborgen ist, was bedeutet, dass nur die Funktion, in der sie deklariert ist, sie erreichen kann.

Das Inlining der Funktion ändert nichts:

inline void doSomething()
{
   static int value ;
}

Es wird nur eine versteckte globale Variable geben. Die Tatsache, dass der Compiler versucht, den Code einzubinden, ändert nichts an der Tatsache, dass es nur eine globale versteckte Variable gibt.

Wenn Ihre Funktion nun als statisch deklariert ist:

static void doSomething()
{
   static int value ;
}

Dann ist es für jede Kompilierungseinheit "privat", was bedeutet, dass jede CPP-Datei einschließlich des Headers, in dem die statische Funktion deklariert ist, eine eigene private Kopie der Funktion hat, einschließlich ihrer eigenen privaten Kopie der globalen versteckten Variablen, also so viele Variablen wie Es gibt Kompilierungseinheiten einschließlich des Headers.

Hinzufügen von "Inline" zu einer "statischen" Funktion mit einer "statischen" Variablen im Inneren:

inline static void doSomething()
{
   static int value ;
}

hat das gleiche Ergebnis, als dieses "Inline" -Schlüsselwort nicht hinzuzufügen, was die darin enthaltene statische Variable betrifft.

Das Verhalten von VC ++ ist also korrekt, und Sie verwechseln die wahre Bedeutung von "Inline" und "Static".

paercebal
quelle
Ich denke, Sie vermissen einen wichtigen Punkt, um zu erwähnen, dass in der Verknüpfungsphase alle statischen Variablen, die in der Inline-Funktion deklariert wurden, in eine aufgelöst würden. Liege ich falsch?
user14416
1
Nein, da sich jede statische Variable in einer eigenen Funktion befindet: Obwohl die Funktionen denselben Namen haben, sind sie intern verknüpft und werden daher nicht über mehrere Übersetzungseinheiten hinweg gemeinsam genutzt.
Paercebal
1
@paercebal in inline void doSomething() { static int value ; }, die Funktion hat externe Verknüpfung; Dies ist eine ODR-Verletzung, wenn sie in einem Header angezeigt wird, der aus zwei verschiedenen Einheiten stammt
MM
@MM was meinst du? Ihre Funktion ist inline, es kann nicht ODR verletzen.
Ruslan
@ Ruslan, das ist eine Nicht-Sequenzierung
MM
38

Ich glaube, der Compiler erstellt viele Kopien der Variablen, aber der Linker wählt eine aus und lässt alle anderen darauf verweisen. Ich hatte ähnliche Ergebnisse, als ich ein Experiment versuchte, um verschiedene Versionen einer Inline-Funktion zu erstellen. Wenn die Funktion nicht tatsächlich inline war (Debug-Modus), gingen alle Aufrufe an dieselbe Funktion, unabhängig von der Quelldatei, aus der sie aufgerufen wurden.

Denken Sie einen Moment wie ein Compiler - wie könnte es anders sein? Jede Kompilierungseinheit (Quelldatei) ist unabhängig von den anderen und kann separat kompiliert werden. Jeder muss daher eine Kopie der Variablen erstellen, da er denkt, dass dies die einzige ist. Der Linker kann diese Grenzen überschreiten und die Referenzen sowohl für Variablen als auch für Funktionen anpassen.

Mark Ransom
quelle
2
AFAICT, Sie haben vollkommen recht mit dem, was Sie hier sagen. Ich verstehe nicht, warum die Leute diese Antwort ablehnen. Meine einzige Vermutung ist, dass sie bis zu "viele Kopien der Variablen" lesen und dann aufhören! :( Wie auch immer ein Token (+1) von mir.
Richard Corden
3
Wenn Leute fragen, was der Compiler tut, meinen sie den Compiler + den Linker, da Sie keine Objektdateien ausführen können. Diese Antwort ist also richtig, aber völlig bedeutungslos.
Evan Dark
1
Weil die Leute unwissend sind. Dies ist eine fortgeschrittenere Frage, und alle sollen in der Diskussion den Unterschied machen.
Sogartar
13

Ich fand die Antwort von Mark Ransom hilfreich - dass der Compiler viele Kopien der statischen Variablen erstellt, der Linker jedoch eine auswählt und sie für alle Übersetzungseinheiten erzwingt.

An anderer Stelle fand ich Folgendes:

Siehe [dcl.fct.spec] / 4

[..] Eine Inline-Funktion mit externer Verknüpfung muss in allen Übersetzungseinheiten dieselbe Adresse haben. Eine statische lokale Variable in einer externen Inline-Funktion bezieht sich immer auf dasselbe Objekt. Ein Zeichenfolgenliteral in einer externen Inline-Funktion ist dasselbe Objekt in verschiedenen Übersetzungseinheiten.

Ich habe keine Kopie des Standards zu überprüfen, aber sie stimmt mit meiner Erfahrung bei der Prüfung der Baugruppe in VS Express 2008 überein

Matt
quelle
5

Es soll so sein. "static" teilt dem Compiler mit, dass die Funktion lokal für die Kompilierungseinheit sein soll. Daher möchten Sie eine Kopie pro Kompilierungseinheit und eine Kopie der statischen Variablen pro Instanz der Funktion.

"inline" wird verwendet, um dem Compiler mitzuteilen, dass die Funktion inline sein soll. Heutzutage wird nur angenommen, dass "es in Ordnung ist, wenn mehrere Kopien des Codes vorhanden sind, stellen Sie einfach sicher, dass es sich um dieselbe Funktion handelt". Jeder teilt also die statischen Variablen.

Hinweis: Diese Antwort wurde als Antwort auf die Antwort geschrieben, die das Originalplakat an sich selbst gesendet hat.

Raphaël Saint-Pierre
quelle
1
Er fragt nach 'statischen Variablen' in einer 'Inline-Funktion', nicht nach Variablen in einer statischen Funktion.
Richard Corden
Wir sind uns einig, aber Sie haben Recht: Eine Bearbeitung ist erforderlich, um die Antwort wieder in den Kontext zu stellen.
Raphaël Saint-Pierre
Ich bin auch darauf gestoßen. Also, welcher von beiden ist es? inlinebewirkt, dass die Funktion inline ist oder es in Ordnung ist, mehrere Kopien zu haben?
Vassilis
@Vassilis beide haben Recht, verursacheninline jedoch kein Inlining, sondern schlagen es nur vor und ermöglichen mehr als eine Definition (jedoch nicht in derselben Kompilierungseinheit).
Raphaël Saint-Pierre
3

Seit ich die Frage geschrieben habe, habe ich sie mit Visual Studio 2008 ausprobiert. Ich habe versucht, alle Optionen zu aktivieren, mit denen VS in Übereinstimmung mit Standards funktioniert, aber es ist möglich, dass ich einige verpasst habe. Dies sind die Ergebnisse:

Wenn die Funktion nur "inline" ist, gibt es nur eine Kopie der statischen Variablen.

Wenn die Funktion "statisch inline" ist, gibt es so viele Kopien wie Übersetzungseinheiten.

Die eigentliche Frage ist nun, ob die Dinge so sein sollen oder ob dies eine Ideosynkratie des Microsoft C ++ - Compilers ist.

Dirk Groeneveld
quelle
1
"Wenn die Funktion" statisch inline "ist," - Ihr ursprünglicher Beitrag hat nichts dazu gesagt. Sie sollten unterschiedliche Ergebnisse erwarten, da statisch für eine Funktion eine andere Bedeutung hat als statisch für eine Variable. statisch für eine Funktion bedeutet, dass andere Übersetzungseinheiten diese Definition nicht sehen.
Windows-Programmierer
Sie sind sich Ihrer Einstellungen nicht sicher, aber der Compiler funktioniert in diesem Fall ordnungsgemäß. Möglicherweise möchten Sie jedoch einen Komponententest einschließen, falls Sie später auf einen nicht konformen Compiler stoßen.
Robert Gould
-1

Inlining bedeutet, dass ausführbarer Code (Anweisungen) in den Code der aufrufenden Funktion eingefügt wird. Der Compiler kann dies unabhängig davon tun, ob Sie darum gebeten haben. Dies hat keine Auswirkungen auf die in der Funktion deklarierten Variablen (Daten).

Windows-Programmierer
quelle
-1

Abgesehen von Designproblemen kann dies alles bedeuten, da Sie bereits daran festhalten, sollten Sie in diesem Fall statisch und nicht inline verwenden. Auf diese Weise teilen alle die gleichen Variablen. (Statische Funktion)

Robert Gould
quelle
-2

Ich glaube, Sie werden am Ende eine pro Übersetzungseinheit haben. Sie haben effektiv viele Versionen dieser Funktion (und ihrer deklarierten statischen Variablen), eine für jede Übersetzungseinheit, die den Header enthält.

Jason Etheridge
quelle
-2

Statisch bedeutet, dass eine Kopie im gesamten Programm verteilt ist. Inline bedeutet jedoch, dass derselbe Code mehrere Male im selben Programm benötigt wird, sodass es nicht möglich ist, eine Variable innerhalb der Inline-Funktion statisch zu machen.


quelle