Warum befinden sich C ++ - Inline-Funktionen im Header?

120

Hinweis: Hier geht es nicht darum, wie Inline-Funktionen verwendet werden oder wie sie funktionieren, sondern darum, warum sie so ausgeführt werden, wie sie sind.

Die Deklaration einer Klassenmitgliedsfunktion muss keine Funktion als definieren inline, sondern ist nur die tatsächliche Implementierung der Funktion. Zum Beispiel in der Header-Datei:

struct foo{
    void bar(); // no need to define this as inline
}

Warum also die Inline - Implementierung eines Klassen funktionieren müssen , um in der Header - Datei? Warum kann ich die Inline-Funktion nicht in die .cppDatei einfügen? Wenn ich versuchen würde, die Inline-Definition in die .cppDatei einzufügen, würde ich einen Fehler in der Art von: erhalten.

error LNK2019: unresolved external symbol 
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main 
1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe 
: fatal error LNK1120: 1 unresolved externals
Thecoshman
quelle
@ Charles Ich würde sagen, dass der zweite Link ähnlich ist, aber ich frage mehr nach der Logik, warum Inline so funktioniert, wie es funktioniert.
Thecoshman
2
In diesem Fall haben Sie möglicherweise entweder "Inline" - oder "Header-Dateien" falsch verstanden. Keine Ihrer Behauptungen ist wahr. Sie können eine Inline-Implementierung einer Member-Funktion durchführen und Inline-Funktionsdefinitionen in eine Header-Datei einfügen. Dies ist möglicherweise keine gute Idee. Können Sie Ihre Frage klären?
CB Bailey
Nach der Bearbeitung fragen Sie möglicherweise nach Situationen, in denen inlineeine Definition angezeigt wird, aber keine vorherige Erklärung, und umgekehrt . Wenn ja, kann dies helfen: stackoverflow.com/questions/4924912/…
CB Bailey

Antworten:

122

Die Definition einer inlineFunktion muss nicht in einer Header-Datei enthalten sein, sondern aufgrund der One Definition Rule ( ODR ). für Inline-Funktionen muss jedoch in jeder Übersetzungseinheit, die sie verwendet, eine identische Definition für die Funktion vorhanden sein.

Der einfachste Weg, dies zu erreichen, besteht darin, die Definition in eine Header-Datei einzufügen.

Wenn Sie die Definition einer Funktion in eine einzelne Quelldatei einfügen möchten, sollten Sie sie nicht deklarieren inline. Eine nicht deklarierte Funktioninline bedeutet nicht, dass der Compiler die Funktion nicht einbinden kann.

Ob Sie eine Funktion deklarieren sollten inlineoder nicht, ist normalerweise eine Entscheidung, die Sie treffen sollten, basierend auf der Version der Regeln für eine Definition , deren Befolgung für Sie am sinnvollsten ist. Das Hinzufügen inlineund anschließende Einschränken durch die nachfolgenden Einschränkungen ist wenig sinnvoll.

CB Bailey
quelle
Kompiliert der Compiler jedoch nicht die CPP-Datei, die die H-Dateien enthält, so dass beim Kompilieren einer CPP-Datei sowohl die Verzögerung als auch die Quelldateien vorhanden sind. Andere eingezogene Header-Dateien sind nur ihre, damit der Compiler darauf vertrauen kann, dass diese Funktionen vorhanden sind und in einer anderen Quelldatei implementiert werden
thecoshman
1
Dies ist tatsächlich eine viel bessere Antwort als meine, +1von mir!
sbi
2
@thecoshman: Es gibt zwei Unterschiede. Quelldatei vs Header-Datei. Konventionell bezieht sich eine Header-Datei normalerweise auf eine Quelldatei, die nicht die Basis für eine Übersetzungseinheit ist, sondern nur # von anderen Quelldateien enthalten ist. Dann gibt es Deklaration gegen Definition. Sie können Deklarationen oder Definitionen von Funktionen entweder in Header-Dateien oder in "normalen" Quelldateien haben. Ich fürchte, ich bin mir nicht sicher, was Sie in Ihrem Kommentar fragen.
CB Bailey
Mach dir keine Sorgen, ich verstehe, warum es jetzt ist ... obwohl ich nicht sicher bin, wer diese Frage wirklich beantwortet hat. Eine Kombination aus Ihrer und der Antwort von @ Xanatos hat es mir erklärt.
Thecoshman
113

Es gibt zwei Möglichkeiten, es zu betrachten:

  1. Inline-Funktionen werden im Header definiert, da der Compiler in der Lage sein muss, den Funktionskörper zu sehen, um einen Funktionsaufruf inline zu machen. Damit ein naiver Compiler dies tun kann, muss sich der Funktionskörper in derselben Übersetzungseinheit wie der Aufruf befinden. (Ein moderner Compiler kann über mehrere Übersetzungseinheiten hinweg optimieren. Daher kann ein Funktionsaufruf eingefügt werden, obwohl sich die Funktionsdefinition in einer separaten Übersetzungseinheit befindet. Diese Optimierungen sind jedoch teuer, nicht immer aktiviert und wurden von der nicht immer unterstützt Compiler)

  2. Im Header definierte Funktionen müssen markiert werden, inlineda andernfalls jede Übersetzungseinheit, die den Header enthält, eine Definition der Funktion enthält und der Linker sich über mehrere Definitionen beschwert (ein Verstoß gegen die One Definition Rule). Das inlineSchlüsselwort unterdrückt dies, sodass mehrere Übersetzungseinheiten (identische) Definitionen enthalten können.

Die beiden Erklärungen laufen wirklich darauf hinaus, dass das inlineSchlüsselwort nicht genau das tut, was Sie erwarten würden.

Ein C ++ - Compiler kann die Inlining- Optimierung (einen Funktionsaufruf durch den Hauptteil der aufgerufenen Funktion ersetzen , den Aufrufaufwand sparen) jederzeit anwenden , sofern dies das beobachtbare Verhalten des Programms nicht verändert.

Das inlineSchlüsselwort macht es einfacher für den Compiler diese Optimierung anzuwenden, um die Funktionsdefinition ermöglicht in mehreren Übersetzungseinheiten sichtbar zu sein, aber das Schlüsselwort bedeutet nicht , den Compiler hat die Funktion inline, und nicht das Schlüsselwort nicht Verbieten Sie dem Compiler, die Funktion einzuschleusen.

jalf
quelle
23

Dies ist eine Grenze des C ++ - Compilers. Wenn Sie die Funktion in den Header einfügen, können alle CPP-Dateien, in die sie eingefügt werden können, die "Quelle" Ihrer Funktion sehen, und das Inlining kann vom Compiler durchgeführt werden. Andernfalls müsste das Inlining vom Linker durchgeführt werden (jede cpp-Datei wird separat in einer obj-Datei kompiliert). Das Problem ist, dass es viel schwieriger wäre, dies im Linker zu tun. Ein ähnliches Problem besteht bei "Vorlagen" -Klassen / -Funktionen. Sie müssen vom Compiler instanziiert werden, da der Linker Probleme haben würde, sie zu instanziieren (eine spezielle Version davon zu erstellen). Einige neuere Compiler / Linker können eine Kompilierung / Verknüpfung mit zwei Durchgängen durchführen, wobei der Compiler einen ersten Durchgang ausführt. Dann erledigt der Linker seine Arbeit und ruft den Compiler auf, um ungelöste Probleme zu lösen (Inline / Vorlagen ...).

Xanatos
quelle
Oh ich verstehe! Ja, es ist nicht für die Klasse selbst, die die Inline-Funktion verwendet, sondern für den anderen Code, der die Inline-Funktionen verwendet. Sie sehen nur die Header-Datei für die Klasse, die inline ist!
Thecoshman
11
Ich bin mit dieser Antwort nicht einverstanden, es ist kein C ++ - Compiler-Limit. es ist nur, wie die Sprachregeln spezifiziert sind. Die Sprachregeln ermöglichen ein einfaches Kompilierungsmodell, verbieten jedoch keine alternativen Implementierungen.
CB Bailey
3
Ich stimme @Charles zu. Tatsächlich gibt es Compiler, die Inline-Funktionen über Übersetzungseinheiten hinweg ausführen, sodass dies definitiv nicht auf Compiler-Einschränkungen zurückzuführen ist.
sbi
5
Obwohl diese Antwort einige technische Fehler zu haben scheint, hat sie mir geholfen zu sehen, wie der Compiler mit Header-Dateien und dergleichen funktioniert.
Thecoshman
10

Der Grund ist, dass der Compiler die Definition tatsächlich sehen muss, um sie anstelle des Aufrufs ablegen zu können.

Denken Sie daran, dass C und C ++ ein sehr vereinfachtes Kompilierungsmodell verwenden, bei dem der Compiler immer nur eine Übersetzungseinheit gleichzeitig sieht. (Dies schlägt beim Export fehl. Dies ist der Hauptgrund, warum nur ein Anbieter es tatsächlich implementiert hat.)

sbi
quelle
9

Das inlineSchlüsselwort c ++ ist irreführend und bedeutet nicht "Inline dieser Funktion". Wenn eine Funktion als Inline definiert ist, bedeutet dies einfach, dass sie mehrmals definiert werden kann, solange alle Definitionen gleich sind. Es ist völlig legal, dass eine Funktion inlineals echte Funktion markiert ist, die aufgerufen wird, anstatt an der Stelle, an der sie aufgerufen wird, Code inline zu setzen.

Das Definieren einer Funktion in einer Header-Datei ist für Vorlagen erforderlich, da beispielsweise eine Vorlagenklasse keine Klasse ist, sondern eine Vorlage für eine Klasse, von der Sie mehrere Variationen vornehmen können. Damit der Compiler zB eine Foo<int>::bar()Funktion erstellen kann, wenn Sie die Foo-Vorlage zum Erstellen einer Foo-Klasse verwenden , muss die tatsächliche Definition von Foo<T>::bar()sichtbar sein.

Erik
quelle
Und da es sich um eine Vorlage für eine Klasse handelt , wird sie nicht als Vorlagenklasse , sondern als Klassenvorlage bezeichnet .
sbi
4
Der erste Absatz ist völlig richtig (und ich wünschte, ich könnte "irreführend" betonen), aber ich sehe keine Notwendigkeit für die Nicht-Sequenzierung in Vorlagen.
Thomas Edleson
Einige Compiler verwenden es als Hinweis darauf, dass die Funktion wahrscheinlich inline sein kann, aber es ist nicht garantiert, dass sie inline ist, nur weil Sie sie deklarieren inline(und wenn Sie sie nicht deklarieren inline, wird nicht garantiert, dass sie nicht inline ist).
Keith M
4

Ich weiß, dass dies ein alter Thread ist, dachte aber, ich sollte das externSchlüsselwort erwähnen . Ich bin kürzlich auf dieses Problem gestoßen und habe es wie folgt gelöst

Helper.h

namespace DX
{
    extern inline void ThrowIfFailed(HRESULT hr);
}

Helper.cpp

namespace DX
{
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            std::stringstream ss;
            ss << "#" << hr;
            throw std::exception(ss.str().c_str());
        }
    }
}
flamewave000
quelle
6
Dies führt im Allgemeinen nicht dazu, dass die Funktion tatsächlich eingebunden wird, es sei denn, Sie verwenden die WPO (Whole Program Optimization).
Chuck Walbourn
3

Weil der Compiler sie sehen muss, um sie inline zu machen. Und Header-Dateien sind die "Komponenten", die üblicherweise in anderen Übersetzungseinheiten enthalten sind.

#include "file.h"
// Ok, now me (the compiler) can see the definition of that inline function. 
// So I'm able to replace calls for the actual implementation.
Leandro TC Melo
quelle
1

Inline-Funktionen

In C ++ ist ein Makro nichts anderes als eine Inline-Funktion. Jetzt haben Makros die Kontrolle über den Compiler.

  • Wichtig : Wenn wir eine Funktion innerhalb der Klasse definieren, wird sie automatisch zu Inline

Die Code of Inline-Funktion wird an der Stelle ersetzt, an der sie aufgerufen wird, wodurch der Overhead der aufrufenden Funktion verringert wird.

In einigen Fällen kann das Inlining der Funktion nicht funktionieren, wie z

  • Wenn statische Variable innerhalb der Inline-Funktion verwendet wird.

  • Wenn die Funktion kompliziert ist.

  • Bei rekursivem Funktionsaufruf

  • Wenn die Adresse der Funktion implizit oder explizit übernommen wurde

Funktionen, die außerhalb der Klasse wie folgt definiert sind, können inline werden

inline int AddTwoVar(int x,int y); //This may not become inline 

inline int AddTwoVar(int x,int y) { return x + y; } // This becomes inline

Innerhalb der Klasse definierte Funktionen werden ebenfalls inline

// Inline SpeedMeter functions
class SpeedMeter
{
    int speed;
    public:
    int getSpeed() const { return speed; }
    void setSpeed(int varSpeed) { speed = varSpeed; }
};
int main()
{
    SpeedMeter objSM;
    objSM.setSpeed(80);
    int speedValue = A.getSpeed();
} 

Hier werden sowohl die Funktionen getSpeed ​​als auch setSpeed ​​inline

Saurabh Raoot
quelle
eh, ein paar nette Infos vielleicht, aber versucht nicht wirklich zu erklären warum . Vielleicht tust du es, aber machst es einfach nicht klar.
Thecoshman
2
Die folgende Aussage ist nicht wahr: "Wichtig: Wenn wir eine Funktion innerhalb der Klasse definieren, wird sie automatisch zu Inline." Selbst wenn Sie "inline" in die Deklaration / Definition schreiben, können Sie sicher sein, dass sie tatsächlich inline ist. Nicht einmal für Vorlagen. Vielleicht haben Sie gemeint, dass der Compiler automatisch das Schlüsselwort "inline" annimmt, aber nicht folgen muss, und mir ist aufgefallen, dass er in den meisten Fällen solche In-Header-Definitionen nicht inline macht, auch nicht für einfache constexpr-Funktionen mit Grundrechenarten.
Pablo Ariel
Hey, danke für die Kommentare ... Nachfolgend finden Sie Zeilen aus Thinking in C ++ micc.unifi.it/bertini/download/programmazione/… Page 400 .. Bitte überprüfen Sie .. Bitte stimmen Sie zu, wenn Sie damit einverstanden sind. Danke ..... Inlines innerhalb von Klassen Um eine Inline-Funktion zu definieren, müssen Sie der Funktionsdefinition normalerweise das Inline-Schlüsselwort voranstellen. Dies ist jedoch innerhalb einer Klassendefinition nicht erforderlich. Jede Funktion, die Sie innerhalb einer Klassendefinition definieren, ist automatisch eine Inline.
Saurabh Raoot
Die Autoren dieses Buches können behaupten, was sie wollen, weil sie Bücher schreiben und keinen Code. Dies musste ich gründlich analysieren, damit meine tragbaren 3D-Demos in weniger als 64 KB passen, indem ich Inline-Code so weit wie möglich vermeide. Beim Programmieren geht es um Fakten und nicht um Religion. Es spielt also keine Rolle, ob ein "Gott-Programmierer" dies in einem Buch gesagt hat, wenn es nicht darstellt, was in der Praxis passiert. Und die meisten C ++ - Bücher gibt es eine Sammlung von schlechten Ratschlägen, in denen Sie von Zeit zu Zeit einen netten Trick finden, den Sie Ihrem Repertoire hinzufügen können.
Pablo Ariel
Hey @PabloAriel Danke ... Bitte analysieren Sie und lassen Sie es mich wissen. Ich bin in
Ordnung,