Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Das inline Schlüsselwort schlägt vor, dass der Compiler anstelle jedes Aufrufs dieser Funktion den Code innerhalb der Funktionsdefinition ersetzt.
Die Verwendung von Inlinefunktionen kann theoretisch das Programm schneller machen, da so der Mehraufwand vermieden wird, der Funktionsaufrufen zugeordnet ist. Zum Aufrufen einer Funktion muss die Absenderadresse in den Stapel gelegt werden, Argumente in den Stapel gelegt werden, zum Funktionskörper gesprungen werden und nach Abschluss der Funktion eine Return-Anweisung ausgeführt werden. Dieser Prozess wird durch das Inlining der Funktion eliminiert. Der Compiler bietet auch unterschiedliche Möglichkeiten zum Optimieren erweiterter Inlinefunktionen im Vergleich zu denen, die nicht vorhanden sind. Ein Kompromiss von Inlinefunktionen besteht darin, dass die Gesamtgröße Ihres Programms erhöht werden kann.
Die Inlinecodeersetzung erfolgt nach Ermessen des Compilers. Der Compiler weist beispielsweise keine Funktion inline auf, wenn seine Adresse verwendet wird oder der Compiler entscheidet, dass sie zu groß ist.
Das inline Schlüsselwort und die One Definition Rule (ODR)
Die ursprüngliche Bedeutung von inline ist ein Hinweis auf den Compiler, um die Codeerweiterung auf der Aufrufwebsite über Funktionsaufrufanweisungen zu bevorzugen. Dies bleibt eine der Bedeutungen von inline.
inline Das Schlüsselwort hat jedoch auch Auswirkungen auf die One Definition Rule (ODR). Normalerweise kann eine Funktion nur einmal in allen Übersetzungseinheiten definiert werden. Wenn eine Funktion markiert inlineist, kann sie in mehreren Übersetzungseinheiten (in der Regel über eine Headerdatei) definiert werden, wenn alle Definitionen identisch sind. Der Linker wählt dann eine Definition aus und verwirft die Duplikate, anstatt einen Fehler zu melden.
Diese duale Art von inline– sowohl als Optimierungshinweis als auch als ODR-Mechanismus – kann Verwirrung verursachen. Der ODR-Aspekt ist eine praktische Notwendigkeit, bei der derselbe Header (mit einer Inlinefunktionsdefinition) in mehreren Quelldateien enthalten sein kann.
Implizite Inlinefunktionen
Bestimmte Funktionen sind implizit inline ohne Das Schlüsselwort erforderlich:
Im Klassenbereich definierte Funktionen: Eine im Textkörper einer Klassendeklaration definierte Funktion ist implizit eine Inlinefunktion. Auf diese Weise können kleine Accessorfunktionen direkt in Klassendefinitionen definiert werden, ohne dass Funktionsaufrufaufwand entsteht – eine Priorität seit den frühen Tagen von C++.
constexprFunktionen: In C++11 eingeführte Funktionen sind implizitinlinedeklariertconstexpr. DaconstexprFunktionen in der Regel in Headerdateien definiert sind, um die Kompilierungszeitauswertung zu ermöglichen, müssen sie dieselben ODR-Regeln wie Inlinefunktionen befolgen.constevalFunktionen: In C++20 eingeführte Funktionen sind implizitinlinedeklariertconsteval.
Inlinevariablen (C++17)
C++17 erweiterte das inline Schlüsselwort auf Variablen. Eine inline Variable kann in mehreren Übersetzungseinheiten definiert werden, und wie Inlinefunktionen wählt der Linker eine Definition aus und verwirft den Rest.
Inlinevariablen sind hilfreich zum Definieren von Konstanten oder statischen Datenmembern in Headerdateien:
// constants.h
inline constexpr double pi = 3.14159265358979323846;
struct MyClass
{
static inline int instanceCount = 0; // Can be defined in header
};
Vor C++17 benötigten solche Variablen eine separate Definition in einer einzelnen Quelldatei, um Verknüpfungsfehler zu vermeiden.
Beispiel: Inlineklassenmememmfunktionen
In der folgenden Klassendeklaration ist der Account Konstruktor eine Inlinefunktion, da er im Textkörper der Klassendeklaration definiert ist. Die Memberfunktionen GetBalance, Deposit und Withdraw werden in ihren Definitionen inline angegeben. Das inline Schlüsselwort ist in den Funktionsdeklarationen in der Klassendeklaration optional.
// account.h
class Account
{
public:
Account(double initial_balance)
{
balance = initial_balance;
}
double GetBalance() const;
double Deposit(double amount);
double Withdraw(double amount);
private:
double balance;
};
inline double Account::GetBalance() const
{
return balance;
}
inline double Account::Deposit(double amount)
{
balance += amount;
return balance;
}
inline double Account::Withdraw(double amount)
{
balance -= amount;
return balance;
}
Hinweis
In der Klassendeklaration wurden die Funktionen ohne das inline-Schlüsselwort deklariert. Das inline Schlüsselwort inline kann in der Klassendeklaration bezeichnet werden; das Ergebnis ist dasselbe.
Eine angegebene Inlinememberfunktion muss in jeder Kompilierungseinheit auf die gleiche Weise deklariert werden. Es muss genau eine Definition einer Inlinefunktion geben.
Eine Klassenmemberfunktion führt standardmäßig zu externer Bindung, es sei denn, eine Definition für diese Funktion enthält den inline-Spezifizierer. Im vorherigen Beispiel wird gezeigt, dass Sie diese Funktionen nicht explizit mit dem inline-Spezifizierer deklarieren müssen. Die Verwendung inline in der Funktionsdefinition schlägt dem Compiler vor, ihn als Inlinefunktion zu behandeln. Sie können eine Funktion jedoch nicht als inline nach einem Aufruf dieser Funktion neu definieren.
inline, __inline und __forceinline
Die Spezifizierer inline und __inline schlagen dem Compiler vor, eine Kopie des Funktionstexts an jeder Stelle einzufügen, an der die Funktion aufgerufen wird.
Die Einfügung (bezeichnet als Inlineerweiterung oder Inlinekonstrukt) wird nur ausgeführt, wenn die Kosten-Nutzen-Analyse des Compilers dies als sinnvoll bewertet. Eine Inlineerweiterung minimiert den Funktionsaufruf-Mehraufwand, möglicherweise auf Kosten der größeren Codegröße.
Das __forceinline Schlüsselwort (oder [msvc::forceinline] Attribut) setzt die Kosten-Nutzen-Analyse außer Kraft und basiert stattdessen auf dem Urteil des Programmierers. Sie sollten __forceinline mit Vorsicht verwenden. Die wahllose Verwendung von __forceinline kann zu längerem Code mit nur marginalen Leistungssteigerungen oder in einigen Fällen sogar mit Leistungsverlusten führen (beispielsweise aufgrund des erweiterten Pagings einer größeren ausführbaren Datei).
Der Compiler behandelt die Inlineerweiterungsoptionen und -Schlüsselwörter als Vorschläge. Es gibt keine Garantie, dass Funktionen inline gestellt werden. Sie können den Compiler nicht zwingen, eine bestimmte Funktion inline zu setzen, auch nicht mit dem __forceinline-Schlüsselwort. Beim Kompilieren mit /clr setzt der Compiler eine Funktion nicht inline, wenn auf die Funktion Sicherheitsattribute angewendet werden.
Kompatibilität mit früheren Versionen und Synonyme für __inline bzw __forceinline . (zwei führende Unterstriche), es sei denn, die Compileroption /Za (Spracherweiterungen deaktivieren) wird angegeben._forceinline_inline
Das inline-Schlüsselwort inline teilt dem Compiler mit, dass eine Inlineerweiterung bevorzugt wird. Der Compiler kann dies jedoch ignorieren. Zwei Fälle, in denen dieses Verhalten auftreten kann, sind:
- Rekursive Funktionen.
- Funktionen, auf die durch einen Zeiger an anderer Stelle in der Übersetzungseinheit verwiesen wird.
Diese und andere Gründe können je nach Feststellung des Compilers die Inlining-Erstellung beeinträchtigen. Verlassen Sie sich nicht darauf, dass der inline-Spezifizierer eine Funktion inline einfügt.
Anstatt eine in einer Headerdatei definierte Inlinefunktion zu erweitern, erstellt der Compiler sie möglicherweise als aufrufbare Funktion in mehr als einer Übersetzungseinheit. Der Compiler kennzeichnet die generierte Funktion für den Linker, um ODR-Verletzungen (One-Definition-Rule) zu verhindern.
Wie bei normalen Funktionen gibt es keine definierte Reihenfolge für die Argumentauswertung in einer Inlinefunktion. Tatsächlich kann es sich von der Argumentauswertungsreihenfolge unterscheiden, wenn sie mithilfe des normalen Funktionsaufrufprotokolls übergeben wird.
Verwenden Sie die /Ob Compileroptimierungsoption, um zu beeinflussen, ob die Inlinefunktionserweiterung tatsächlich auftritt.
/LTCG führt modulübergreifendes Inlining durch, unabhängig davon, ob es im Quellcode angefordert wird oder nicht.
Beispiel 1
// inline_keyword1.cpp
// compile with: /c
inline int max(int a, int b)
{
return a < b ? b : a;
}
Die Memberfunktionen einer Klasse können inline deklariert werden, entweder indem das inline-Schlüsselwort verwendet oder die Funktionsdefinition innerhalb der Klassendefinition platziert wird.
Beispiel 2
// inline_keyword2.cpp
// compile with: /EHsc /c
#include <iostream>
class MyClass
{
public:
void print() { std::cout << i; } // Implicitly inline
private:
int i;
};
Microsoft-spezifisch
Das __inline-Schlüsselwort ist äquivalent zu inline.
Der Compiler kann sogar mit __forceinline keine Funktion inline setzen, wenn Folgendes der Fall ist:
- Die Funktion oder ihr Aufrufer werden mit
/Ob0kompiliert (die Standardoption für Debugbuilds). - Die Funktion und der Aufrufer verwenden unterschiedliche Typen der Ausnahmebehandlung (C++-Ausnahmebehandlung zum einen, strukturierte Ausnahmebehandlung zum anderen).
- Die Funktion weist eine variable Argumentliste auf.
- Die Funktion verwendet eine Inlineassembly, es sei denn, sie wird mit
/Ox,/O1, oder/O2kompiliert. - Die Funktion ist rekursiv und hat
#pragma inline_recursion(on)nicht festgelegt. Mit dem Pragma werden rekursive Funktionen mit einer Standardtiefe von 16 Aufrufen inline gesetzt. Verwenden Sieinline_depthPragma, um die Inliningtiefe zu reduzieren. - Die Funktion ist virtuell und wird virtuell aufgerufen. Direkte Aufrufe virtueller Funktionen können inline gesetzt werden.
- Das Programm akzeptiert die Adresse der Funktion, und der Aufruf erfolgt über den Zeiger auf die Funktion. Direkte Aufrufe von Funktionen, deren Adresse akzeptiert wurden, können inline gesetzt werden.
- Die Funktion ist auch mit dem
naked__declspec-Modifizierer gekennzeichnet.
Wenn der Compiler eine Funktion, die mit __forceinline deklariert ist, nicht inline stellen kann, wird eine Warnung der Stufe 1 ausgelöst, es sei denn:
- Die Funktion wird mithilfe von /Od oder /Ob0 kompiliert. In diesen Fällen wird kein Inlining erwartet.
- Die Funktion wird extern, in einer enthaltenen Bibliothek oder einer anderen Übersetzungseinheit definiert oder ist ein virtuelles Anrufziel oder ein indirektes Anrufziel. Der Compiler kann keinen Code ohne Inlining identifizieren, den er in der aktuellen Übersetzungseinheit nicht finden kann.
Rekursive Funktionen können durch Inlinecode in eine durch das inline_depth Pragma angegebene Tiefe ersetzt werden, bis zu maximal 16 Aufrufe. Nach dieser Tiefe werden rekursive Funktionsaufrufe als Aufrufe einer Instanz der Funktion behandelt. Die Tiefe, bis zu der rekursive Funktionen durch die Inlineheuristik geprüft werden, kann 16 nicht überschreiten. Das inline_recursion-Pragma steuert die Inlineerweiterung einer Funktion, die aktuell erweitert wird. Weitere Informationen erhalten Sie unter der (/Ob) Compileroption Inlinefunktionserweiterung.
Der C++-Standard definiert allgemeine Attribute. Außerdem können Compileranbieter ihre eigenen Attribute innerhalb eines anbieterspezifischen Namespaces (in unserem Fall msvc) definieren. Die folgenden microsoftspezifischen Attribute können verwendet werden, um dasLiningverhalten zu steuern:
Microsoft-spezifische Attribute zum Steuern des Inliningverhaltens
| Merkmal | Bedeutung |
|---|---|
[msvc::forceinline] |
Hat dieselbe Bedeutung wie __forceinline. |
[msvc::forceinline_calls] |
Kann an oder vor einer Anweisung oder einem Block platziert werden, um die Inline-Heuristik zu erzwingen, alle Aufrufe in dieser Anweisung oder Block zu erzwingen. |
[msvc::flatten] |
Ähnlich wie [[msvc::forceinline_calls]], aber rekursiv erzwingen alle Aufrufe im Bereich, auf den sie angewendet wird, bis keine Aufrufe verbleiben. |
[msvc::noinline] |
Wenn vor einer Funktionsdeklaration platziert wird, hat die gleiche Bedeutung wie __declspec(noinline). |
[msvc::noinline_calls] |
Kann vor jeder Anweisung platziert oder blockiert werden, um die Inlineierung für alle Aufrufe im Bereich zu deaktivieren, auf den sie angewendet wird. |
Ende Microsoft-spezifisch
Weitere Informationen zum Verwenden des inline-Bezeichners finden Sie unter:
Verwendungsmöglichkeiten von Inlinefunktionen
Inlinefunktionen werden am besten für kleine Funktionen verwendet, z. B. für Funktionen, die Zugriff auf Datenmmber ermöglichen. Kurze Funktionen sind für den Aufwand von Funktionsaufrufen sensibel. Längere Funktionen benötigen proportional weniger Zeit in der Aufruf- und Rückgabesequenz und profitieren weniger vom Inlining.
Eine Point Klasse kann wie folgt definiert werden:
// when_to_use_inline_functions.cpp
// compile with: /c
class Point
{
public:
// Define "accessor" functions
// as reference types.
unsigned& x();
unsigned& y();
private:
unsigned _x;
unsigned _y;
};
inline unsigned& Point::x()
{
return _x;
}
inline unsigned& Point::y()
{
return _y;
}
Die Koordinatenmanipulation ist ein relativ allgemeiner Vorgang in einem Client dieser Klasse und durch die Angabe der beiden Zugriffsmethodenfunktionen (x und y im vorhergehenden Beispiel) als inline wird in der Regel der Mehraufwand gespart bei:
- Funktionsaufrufen (einschließlich der Parameterübergabe und -ablage der Adresse des Objekts auf dem Stapel)
- Beibehaltung des Stapelrahmens des Aufrufers
- Neuem Stapelrahmensetup
- Rückgabewertkommunikation
- Wiederherstellen des alten Stapelrahmens
- Return
Inlinefunktionen im Vergleich zu Makros
Ein Makro hat einige Dinge, die mit einer inline Funktion gemeinsam sind. Es gibt jedoch zwei wichtige Unterschiede. Betrachten Sie das folgende Beispiel:
#include <iostream>
#define mult1(a, b) a * b
#define mult2(a, b) (a) * (b)
#define mult3(a, b) ((a) * (b))
inline int multiply(int a, int b)
{
return a * b;
}
int main()
{
std::cout << (48 / mult1(2 + 2, 3 + 3)) << std::endl; // outputs 33
std::cout << (48 / mult2(2 + 2, 3 + 3)) << std::endl; // outputs 72
std::cout << (48 / mult3(2 + 2, 3 + 3)) << std::endl; // outputs 2
std::cout << (48 / multiply(2 + 2, 3 + 3)) << std::endl; // outputs 2
std::cout << mult3(2, 2.2) << std::endl; // no warning
std::cout << multiply(2, 2.2); // Warning C4244 'argument': conversion from 'double' to 'int', possible loss of data
}
33
72
2
2
4.4
4
Hier sind einige der Unterschiede zwischen dem Makro und der Inlinefunktion:
- Makros werden immer inline erweitert. Eine Inlinefunktion ist jedoch nur inlineiert, wenn der Compiler bestimmt, dass es die optimale Aufgabe ist.
- Das Makro kann zu unerwartetem Verhalten führen, was zu subtilen Fehlern führen kann. Beispielsweise wird der Ausdruck
mult1(2 + 2, 3 + 3)auf2 + 2 * 3 + 3erweitert, der auf 11 ausgewertet wird, aber das erwartete Ergebnis ist 24. Eine scheinbar gültige Lösung besteht darin, Klammern um beide Argumente des Funktionsmakros hinzuzufügen, was dazu#define mult2(a, b) (a) * (b)führt, dass das Problem gelöst wird, aber dennoch überraschendes Verhalten verursachen kann, wenn Ein Teil eines größeren Ausdrucks ist. Dies wurde im vorherigen Beispiel veranschaulicht, und das Problem konnte durch Definieren des Makros wie#define mult3(a, b) ((a) * (b))behoben werden. - Eine Inlinefunktion unterliegt der semantischen Verarbeitung durch den Compiler, während der Präprozessor Makros ohne denselben Vorteil erweitert. Makros sind nicht typsicher, Funktionen jedoch schon.
- Die Ausdrücke, die als Argumente an Inlinefunktionen übergeben werden, werden einmal ausgewertet. In einigen Fällen können die Ausdrücke, die als Argumente an Makros übergeben werden, mehrmals ausgewertet werden. Berücksichtigen Sie beispielsweise Folgendes:
#include <iostream>
#define sqr(a) ((a) * (a))
int increment(int& number)
{
return number++;
}
inline int square(int a)
{
return a * a;
}
int main()
{
int c = 5;
std::cout << sqr(increment(c)) << std::endl; // outputs 30
std::cout << c << std::endl; // outputs 7
c = 5;
std::cout << square(increment(c)) << std::endl; // outputs 25
std::cout << c; // outputs 6
}
30
7
25
6
In diesem Beispiel wird die Funktion increment zweimal aufgerufen, während der Ausdruck sqr(increment(c)) auf ((increment(c)) * (increment(c))) erweitert wird. Dies führte dazu, dass der zweite Aufruf von increment 6 zurückgibt, daher wird der Ausdruck auf 30 ausgewertet. Jeder Ausdruck, der Nebenwirkungen enthält, kann sich auf das Ergebnis auswirken, wenn es in einem Makro verwendet wird, das vollständig erweiterte Makro überprüfen, um zu überprüfen, ob das Verhalten beabsichtigt ist. Wenn stattdessen die Inlinefunktion square verwendet wurde, wird die Funktion increment nur einmal aufgerufen, und das richtige Ergebnis von 25 wird abgerufen.
Siehe auch
noinline
auto_inline
[msvc::forceinline]
[msvc::forceinline_calls]
[msvc::flatten]
[msvc::nolinline]
[msvc::nolinline_calls]