One Definition Rule (ODR) ist eine grundlegende Regel in C++, die vorschreibt, dass es im gesamten Programm (allen Übersetzungseinheiten) für jedes Objekt, jede Funktion oder jede Klasse genau eine definierte Implementierung geben muss.
ODR wird verletzt, wenn:
Dies führt zu schwer zu erkennenden, unvorhersehbaren Bugs, inkorrekter Linkerarbeit oder, noch schlimmer, zu unterschiedlichen Verhaltensweisen des Programms, abhängig davon, wie es zusammengestellt wurde.
Warum kommt es zu Verstößen:
In großen Projekten werden oft .h-Dateien ohne Versionskontrolle kopiert/modifiziert oder der Code wird in viele Module mit separater Kompilierung aufgeteilt. Wenn jemand eine Inline-Funktion nur an einem Ort ändert, können die anderen Quellcodedateien die alte Version haben.
Wie man Verstößen vorbeugt:
constexpr oder, ab C++17, inline-Variablen.Darf man static-Funktionen (static void foo()) mit denselben Namen und unterschiedlicher Implementierung in verschiedenen .cpp-Dateien ohne Konsequenzen definieren?
Viele glauben, dass static-Funktionen untereinander in Modulen keinen Einfluss haben. Die Antwort lautet: ja, das ist erlaubt, da jede von ihnen internes Linkage hat (sichtbar nur innerhalb ihrer Übersetzungseinheit). Für Inline-Funktionen und Vorlagen ist dies jedoch nicht garantiert, was oft verwechselt wird.
Beispiel:
// file1.cpp static void foo() { std::cout << "A"; } // file2.cpp static void foo() { std::cout << "B"; }
Aufrufe in diesen Modulen werden unabhängig sein.
Geschichte
In einem großen Projekt hat ein Entwickler den Körper einer Inline-Funktion nur in einer der Klone der .h-Datei geändert. Nach dem Kompilieren wurde ein Verhalten unvorhersehbar: Einige Module arbeiteten nach dem alten Algorithmus, andere nach dem neuen. Grund war der ODR-Verstoß für die Inline-Funktion.
Geschichte
Bei der Migration auf C++17 wurde eine Konstante in mehreren Header-Dateien ohne Verwendung des Schlüsselworts inline definiert. Bei der Linkerarbeit traten viele Duplicate-Symbol-Fehler auf. Dies wurde behoben, indem die Variable als
inline constdeklariert wurde.
Geschichte
Es wurde spät erkannt, dass die generierte .h-Datei des Build-Systems zwei Implementierungen derselben Methode einer Template-Klasse enthielt, die sich nur durch einen ifdef-Befehl in verschiedenen Builds unterschieden. Später fiel die Anwendung gelegentlich aufgrund von ABI-Abweichungen zwischen den verbundenen Modulen aus.