C ++ Inline-Member-Funktion in der CPP-Datei

75

Ich weiß, dass Inline-Member-Funktionen per Definition in den Header aufgenommen werden sollten. Was aber, wenn es nicht möglich ist, die Implementierung der Funktion in den Header zu setzen? Nehmen wir diese Situation:

Datei Ah

#pragma once
#include "B.h"

class A{
    B b;
};

Datei Bh

#pragma once

class A; //forward declaration

class B{
    inline A getA();
};

Aufgrund des Rundschreibens muss ich die Umsetzung von getAin setzen

B.cpp

#include "B.h"
#include "A.h"

inline A B::getA(){
    return A();
}

Wird der Compiler inline getA? Wenn ja, welches Inline-Schlüsselwort ist das signifikante (das im Header oder das in der CPP-Datei)? Gibt es eine andere Möglichkeit, die Definition einer Inline-Member-Funktion in die CPP-Datei aufzunehmen?

Matte
quelle
Haben Sie den falschen Eindruck und denken, dass das Schlüsselwort inline bedeutet, dass der Compiler eine Inline-Assembly generiert, anstatt einen Funktionsaufruf durchzuführen?
Martin York
nah - ich weiß, was passieren soll
Mat
Bist du sicher. Was denkst du sollte passieren?
Martin York
3
@Matte. Nein, es bedeutet nichts in Bezug auf Inline-Code. Es ist nur ein Hinweis (den alle modernen Compiler ignoriert haben). Der Compiler muss nichts tun. Der Compiler analysiert alle Funktionen auf potenzielles Inlining, unabhängig davon, ob das Schlüsselwort 'inline' verwendet wird oder nicht.
Martin York
3
Ja, ich weiß, dass es nur ein Hinweis ist - trotzdem sagst du, dass das Schlüsselwort "Inline" veraltet ist?
Mat

Antworten:

72

Zitieren aus C ++ FAQ :

Hinweis: Die Definition der Funktion (der Teil zwischen {...}) muss unbedingt in einer Header-Datei platziert werden, es sei denn, die Funktion wird nur in einer einzelnen CPP-Datei verwendet. Insbesondere wenn Sie die Definition der Inline-Funktion in eine CPP-Datei einfügen und sie aus einer anderen CPP-Datei aufrufen, wird vom Linker ein "ungelöster externer" Fehler angezeigt.

Der Compiler muss die Definition der Inline-Funktion sehen, wenn er eine Verwendung dieser Inline-Funktion findet. Dies ist normalerweise möglich, wenn die Inline-Funktion in einer Header-Datei platziert wird.

Wird der Compiler getA inline?

Nein, außer wenn die Verwendung von getA()in B.cpp selbst erfolgt.

Wenn ja, welches Inline-Schlüsselwort ist das signifikante (das in der Kopfzeile oder das in der CPP)?

Best Practice : nur in der Definition außerhalb des Klassenkörpers.

Gibt es eine andere Möglichkeit, die Definition einer Inline-Member-Funktion in die CPP-Datei einzufügen?

Nein, zumindest weiß ich es nicht.

Arun
quelle
19

Es kann nicht außerhalb des Anwendungsbereichs von B.cpp. Der Compiler arbeitet auf einer Basis pro Kompilierungseinheit, dh er kompiliert jede CPP-Datei einzeln. Wenn er also C. CPP kompiliert, ist der Code für getA () nicht verfügbar und er muss einen Funktionsaufruf und ausführen Lassen Sie den Linker das Problem beheben (oder, wenn er Sie wirklich beim Wort genommen und versucht hat, inline zu gehen, hat er einen Linkerfehler. Er inlinehat ähnliche Eigenschaften wie static).

Die einzige Ausnahme ist LTCG, dh die Generierung von Link-Time-Code, die auf neueren Compilern verfügbar ist.

Ein Ansatz in diesem Fall besteht darin, eine andere Header-Datei (manchmal * .inl-Dateien genannt) zu haben, die den Inline-Code enthält.

BEARBEITEN: Welche Inline relevant ist - es ist die in der Klassendefinition, dh in der Header-Datei. Denken Sie daran, dass viele Compiler ihre eigenen Gedanken darüber haben, was eingebunden werden kann und sollte. gcc kann beispielsweise das Inlining vollständig deaktivieren (-O0) oder alles einbinden, was es für inlining hält (wie -O3).

EboMike
quelle
Also - in meinem Beispiel - würde ich B.cpp einfach in B.inl umbenennen und überall, wo ich normalerweise Bh einschließen würde, würde ich stattdessen B.inl einschließen?
Mat
1
Ich würde B.cpp nicht in B.inl umbenennen - ich würde ein separates B.inl (oder vorzugsweise B_inline.h) erstellen und alle inline-Funktionen dort einfügen. Fügen Sie dann in Ihre B.cpp die Datei _inline.h ein.
EboMike
1
"Es kann nicht außerhalb des Geltungsbereichs von B.cpp ..." - Einige der Gründe für das NEIN haben sich möglicherweise geändert, da einige Toolchains über Link Time Optimizations (LTO) verfügen. Es kann aktiviert werden, -fltowenn GCC und einige andere Compiler verwendet werden.
JWW
@jww Ich bin mir nicht sicher, ob du den zweiten Absatz gelesen hast, aber dort habe ich LTCG / LTO erwähnt :) Was jetzt natürlich häufiger ist als 2010, als ich das geschrieben habe. Obwohl ich persönlich immer noch gerne C ++ - Code schreibe, der dem Compiler bei der Optimierung hilft, indem er inlinierbare Funktionen in Header-Dateien einfügt.
EboMike
8

Ich würde dies aus der entgegengesetzten Richtung tun.

Fügen Sie Ihrer Funktion keine Inline-Deklarationen hinzu (es sei denn, Sie benötigen dies auch).

Sie müssen die Inline-Deklaration nur dann zu einer Funktion / Methode hinzufügen, wenn Sie die Funktion in einer Header-Datei definieren, jedoch außerhalb der Klassendeklaration.

Xh

class X
{
    public:
        int getX()   { return 4;} // No inline because it is part of the class.
                                  // The compiler knows that needs an inline tag
        int getY();
        int getZ();
};

inline
int X::getY()  { return 5;}       // This needs to be explicitly declared inline.
                                  // Otherwise the linker will complain about
                                  // multiple definitions in compilation units.

X.cpp

 // Never declare anything inline in the cpp file.

 int X::getZ() { return 6; }

Für Sie spezifischer Fall.
Entfernen Sie alle Inline-Spezifikationen. Sie tun nicht das, was Sie denken, dass sie tun.

Martin York
quelle
1
ah - wenn also eine Funktion innerhalb des Körpers einer Klasse definiert ist, wird sie trotzdem eingefügt?
Mat
1
@Mat: Es wird markiert, als ob das Inline-Schlüsselwort angewendet worden wäre. Verwechseln Sie dies nicht mit dem Inline-Code des Compilers. Alle modernen Compiler ignorieren das Tag vollständig (für den Pedantiker: Der Linker verwendet es (aber nicht zum Inlining)).
Martin York
7

Heutzutage können die meisten Compiler sowohl zur Verknüpfungszeit als auch zur Kompilierungszeit Inlining durchführen. Wenn Ihre Funktion wahrscheinlich vom Inlining profitiert, wird der Link Time-Optimierer wahrscheinlich genau das tun.

Bis der Linker darauf zugreift, ist nicht viel über den Inline-Status der Compilerausgabe verfügbar, außer dass der Compiler bestimmte Objekte als sammelbar kennzeichnet, beispielsweise weil eine Inline-Funktion oder eine Klassenvorlageninstanz in mehreren Kompilierungseinheiten angezeigt wird, oder Es sollte einen Fehler auslösen, wenn mehrere Symbole einen Namen gemeinsam haben, z. B. wenn die Hauptfunktion zweimal definiert wird. Nichts davon hat Einfluss auf den tatsächlichen Code, den es generiert.

SingleNegationElimination
quelle
Ich wusste nicht, dass das möglich ist. Interessiert sich der Linker für das Schlüsselwort "inline" oder entscheidet er selbst, was inline ist?
Mat
2
Ich wäre mir über den "größten" Teil nicht so sicher. Außerdem ist LTCG furchtbar langsam, und ich persönlich mag es, mehr Kontrolle darüber zu haben, was der Compiler tut.
EboMike
1
@EboMike: Die Kontrolle, die du denkst, ist eine Illusion. Der Compiler kann immer besser als Sie entscheiden, was es wert ist, eingefügt zu werden. (Menschen sind schrecklich darin, die erforderliche Analyse durchzuführen). Lassen Sie den Compiler die Arbeit machen, die er machen sollte (Inlining). Lassen Sie den Menschen das tun, was er kann (über Algorithmen nachdenken).
Martin York
1
@Mat: Alle modernen Compiler ignorieren das Inline-Schlüsselwort (in Bezug auf Inline-Code) (es sei denn, Sie erzwingen sie auch mit einem Compiler-Flag und das ist immer ein Fehler (es sei denn, Sie wissen wirklich, was Sie tun und wenn Sie glauben, dass Sie es tun) dann tust du nicht)). Der Linker benötigt das Inline-Schlüsselwort, um zu wissen, dass er mehrere Definitionen ignorieren (oder mehrere Definitionen einer Funktion konsolidieren) soll. Andernfalls muss er davon ausgehen, dass Sie Fehler gemacht haben und einen Fehler generieren.
Martin York
0

Hier ist, wie ich es gemacht habe.

Datei Ah

#pragma once
#include "B.h"

class A {
    B b;
};

Datei Bh

#pragma once

class B {
public:
    template<class T> inline T getA() {
        assert(NULL); // Use 'getA<A>()' template specialization only!
        return NULL;
    }
};

class A; // Forward declaration
template<> inline A B::getA<A>();

Datei Kap

#pragma once
#include "A.h"
#include "B.h"

// Implement template specialization here!
template<> inline A B::getA<A>() { return A(); }

Fügen Sie einfach die Datei "Ch" hinzu, um die Methode getA () verwenden zu können. Die einzige Änderung gegenüber dem ursprünglichen Code besteht darin, dass die Methode getA () als öffentlich statt als privat definiert werden muss.

Wie viele von Ihnen erklärt haben, ist dies jedoch nicht wirklich nützlich.

Pascal Viguié
quelle
0

ISO / IEC 14882: 2014

Jedes Programm muss genau eine Definition jeder Nicht-Inline-Funktion oder -Variable enthalten, die in diesem Programm verwendet wird. Keine Diagnose erforderlich. Die Definition kann explizit im Programm erscheinen, sie befindet sich im Standard oder in einer benutzerdefinierten Bibliothek oder ist (falls zutreffend) implizit definiert (siehe 12.1, 12.4 und 12.8). In jeder Übersetzungseinheit, in der sie verwendet wird, ist eine Inline-Funktion zu definieren.

isnullxbh
quelle