H
flammenvogel schrieb:
inline signalisiert dem Kompiler er soll die Funktionsaufrufe im Quelltext durch denn Quelltext der Funktion erstetzen. Wenn du dann einmal irgendwo inline hinschreibst weiß der Compiler bescheid. <- Da gibt es keinen Unterschied
Sicher gibt es einen Unterschied.
Zum wiederholten Male: Der Compiler kann eine Funktion nur dort inline expandieren wo er die Definition der Funktion sieht. Wenn du eine Funktion also nur in einer cpp-Datei inline deklarierst, dann kann diese Funktion auch nur innerhalb dieser Datei inline expandiert werden. Unproblematisch, aber vielleicht nicht das, was gewollt war.
Kommen wir also zu den möglichen Szenarien:
1. inline Deklaration im Header + Definition der Funktion im Header
inline void func()
{
...
}
// bzw.
class Foo
{
public:
void foo() {...} // inline
};
Diese Variante ist unproblematisch. Überall wo func aufgerufen werden soll ist auch die Definition von func sichtbar. Der Code kann inline expandiert werden. ODR-Verletzungen können nur auftrten, wenn mit bedingter Übersetzung rumgespielt wird.
2. inline Deklaration im Header + Deklaration im Header + Definition in cpp-Datei
// func.h
inline void func();
// func.cpp
#include "func.h"
void func() // identisch zu inline void func
{
}
Diese Variante ist richtig gefährlich.
Erstmal kann func nur innerhalb von func.cpp inline expandiert werden (es sei denn irgendjemand kommt auf die schwachsinnige Idee func.cpp zu inkludieren).
Andere Clients von func müssen func.h inkludieren und gegen func.o linken.
Gleichzeit lauern hier aber ODR-Verletzungen. Da func in func.h inline deklariert wurde, darf func nun "Einmal pro Übersetzungseinheit" definiert werden. Solange jede Definition tokenweise identisch ist.
Das Blöde ist, dass viele Compiler/Linker-Kombinationen die letzte Einschränkung nicht prüfen und man so schnell undefiniertes Verhalten bekommt:
// func.h
inline void func();
// func.cpp
#include "func.h"
void func() // identisch zu inline void func
{
}
// main.cpp
#include "func.h"
void func()
{
cout << "Hallo undefiniertes Verhalten!" << endl;
}
int main()
{
func();
}
main.cpp und func.cpp können da func in func.h inline deklariert wurde zusammen zu einem Programm gelinkt werden. func darf sowohl in main, als auch in func.cpp aufgerufen werden, da beide Dateien die Definition der Inline-Funktion enthalten.
Dummerweise unterscheiden sich die beiden Definitionen von func. Ergebnis: undefiniertes Verhalten.
3. normale Deklaration im Header + inline Definition in cpp-Datei
// func.h
void func();
// func.cpp
inline void func()
{}
Diese Variante ist ebenfalls problematisch.
func kann wieder nur innerhalb von func.cpp inline generiert werden.
Gleichzeitig führt *jeder* Aufruf von func außerhalb von func.cpp zu undefiniertem Verhalten.
Der Standard schreibt vor, dass eine inline-Funktion nur dort aufgerufen werden darf, wo dessen Definition sichtbar ist. Andernfalls ist das Verhalten undefiniert. Sollte func außerhalb von func.cpp erneut definiert werden, bekommt man wiederum undefiniertes Verhalten, falls die neue Definition nicht tokenweise identisch mit der von func.cpp ist.
Fazit:
* Inline kann nur bei der Deklaration, nur bei der Definition oder sowohl bei der Deklaration als auch der Definition einer Funktion auftreten.
* Eine inline-Funktion darf nur dort aufgerufen werden, wo ihre Definition sichtbar ist.
* Auf der sicheren Seite ist man bei inline Funktionen nur, wenn man selbige in einem Header definiert. Wo man dann das inline hinschreibt ist wurscht.
* Merkregel: Definiere inline-Funktionen immer in Header-Dateien.