One Definition Rule (ODR) is een fundamentele regel in C++, die vereist dat er voor elk object, functie of klasse in de gehele programma (alle vertaal-eenheden) precies één definitie (implementation) bestaat.
ODR wordt overtreden als:
Dit leidt tot moeilijk te traceren, onvoorspelbare bugs, onjuiste linking of, nog erger, tot verschillend gedrag van het programma afhankelijk van hoe het is gekoppeld.
Waarom wordt het overtreden:
In grote projecten worden .h-bestanden vaak gekopieerd/modificaties zonder versiecontrole, of de code wordt verdeeld over meerdere modules met afzonderlijke compilatie. Als iemand een inline-functie alleen op één plek wijzigt, kunnen de andere bronbestanden de oude versie hebben.
Hoe te vermijden:
constexpr voor constante variabelen of, vanaf C++17, inline variabelen.Mag je static-functies (static void foo()) met dezelfde naam en verschillende implementaties in verschillende .cpp-bestanden zonder gevolgen definiëren?
Veel mensen denken dat static-functies helemaal geen invloed op elkaar hebben tussen modules. Het antwoord is: ja, dat kan, omdat elke functie interne linkage heeft (zichtbaar alleen binnen zijn eigen vertaal-eenheid). Echter, voor inline-functies en sjablonen is dit niet gegarandeerd, wat vaak verwarring oplevert.
Voorbeeld:
// file1.cpp static void foo() { std::cout << "A"; } // file2.cpp static void foo() { std::cout << "B"; }
Oproepen in deze modules zullen onafhankelijk zijn.
Verhaal
In een groot project wijzigde een ontwikkelaar de body van een inline-functie alleen in een van de klonen van het .h-bestand. Na het bouwen werd bepaald gedrag onvoorspelbaar: sommige modules werkten volgens het oude algoritme, andere volgens het nieuwe. De oorzaak was een schending van de ODR voor de inline-functie.
Verhaal
Bij de migratie naar C++17 werd een constante variabele gedefinieerd in meerdere headerbestanden zonder het sleutelwoord inline te gebruiken. Bij het linken ontstonden veel duplicate symbol fouten. Dit werd opgelost door de variabele als
inline constte declareren.
Verhaal
Te laat ontdekten we dat het gegenereerde .h-bestand van het buildsysteem twee implementaties van dezelfde methode van een sjabloonklasse bevatte, die verschilde door één ifdef-controle in verschillende builds. Later viel de applicatie sporadisch uit vanwege inconsistentie in ABI tussen de gekoppelde modules.