One Definition Rule (ODR) è una regola fondamentale del C++ che richiede che all'interno dell'intero programma (di tutte le unità di traduzione) ci sia esattamente una definizione per ogni oggetto, funzione o classe.
L'ODR viene violata se:
Questo porta a bug difficili da identificare e imprevedibili, linking errati o, peggio, a un comportamento diverso del programma a seconda di come è stato assemblato.
Perché viene violata:
Nei grandi progetti è comune copiare/modificare i file .h senza controllo di versione, o separare il codice in molti moduli con compilazione separata. Se qualcuno modifica una funzione inline solo in un luogo, gli altri file sorgente possono avere una vecchia versione.
Come evitare:
constexpr o, a partire da C++17, variabili inline.È possibile definire funzioni statiche (static void foo()) con nomi uguali e implementazioni diverse in diversi file .cpp senza conseguenze?
Molti pensano che le funzioni statiche non interferiscano tra loro nei moduli. Risposta: sì, è possibile, perché ognuna di esse ha un linkage interno (visibile solo all'interno della propria unità di traduzione). Tuttavia, per le funzioni inline e i template questo non è garantito, e spesso si confondono.
Esempio:
// file1.cpp static void foo() { std::cout << "A"; } // file2.cpp static void foo() { std::cout << "B"; }
Le chiamate in questi moduli saranno indipendenti.
Storia
In un grande progetto, uno sviluppatore ha cambiato il corpo di una funzione inline solo in uno dei cloni del file .h. Dopo la compilazione, alcuni comportamenti sono diventati imprevedibili: parte dei moduli funzionavano con il vecchio algoritmo, parte con il nuovo. Causa: violazione dell'ODR per la funzione inline.
Storia
Durante la migrazione a C++17, una variabile costante è stata definita in diversi file di intestazione senza utilizzare la parola chiave inline. Durante il linking sono comparsi molti errori di simbolo duplicato. È stato corretto dichiarando la variabile come
inline const.
Storia
È stata scoperta in ritardo una situazione in cui il file .h generato dal sistema di build conteneva due implementazioni dello stesso metodo di una classe template, che si differenziavano in base a un controllo ifdef in diverse build. In seguito, l'applicazione è caduta periodicamente a causa di divergenze nell'ABI tra i moduli collegati.