C++
Une règle de définition (ODR)
Recherche…
Multiplier la fonction définie
La conséquence la plus importante de la règle de définition unique est que les fonctions non intégrées avec un lien externe ne doivent être définies qu'une seule fois dans un programme, bien qu'elles puissent être déclarées plusieurs fois. Par conséquent, ces fonctions ne doivent pas être définies dans les en-têtes, car un en-tête peut être inclus plusieurs fois à partir de différentes unités de traduction.
foo.h
:
#ifndef FOO_H
#define FOO_H
#include <iostream>
void foo() { std::cout << "foo"; }
void bar();
#endif
foo.cpp
:
#include "foo.h"
void bar() { std:: cout << "bar"; }
main.cpp
:
#include "foo.h"
int main() {
foo();
bar();
}
Dans ce programme, la fonction foo
est définie dans l'en-tête foo.h
, qui est incluse deux fois: une fois depuis foo.cpp
et une fois depuis main.cpp
. Chaque unité de traduction contient donc sa propre définition de foo
. Notez que les gardes d' foo.h
dans foo.h
n'empêchent pas que cela se produise, puisque foo.cpp
et main.cpp
chacun séparément foo.h
Le résultat le plus probable de la tentative de création de ce programme est une erreur de lien-time identifiant foo
comme ayant été définie par une multiplication.
Pour éviter de telles erreurs, il convient de déclarer les fonctions dans les en-têtes et de les définir dans les fichiers .cpp
correspondants, à quelques exceptions près (voir d'autres exemples).
Fonctions en ligne
Une fonction déclarée en inline
peut être définie dans plusieurs unités de traduction, à condition que toutes les définitions soient identiques. Il doit également être défini dans chaque unité de traduction dans laquelle il est utilisé. Par conséquent, les fonctions en ligne doivent être définies dans les en-têtes et il n'est pas nécessaire de les mentionner dans le fichier d'implémentation.
Le programme se comportera comme s'il y avait une seule définition de la fonction.
foo.h
:
#ifndef FOO_H
#define FOO_H
#include <iostream>
inline void foo() { std::cout << "foo"; }
void bar();
#endif
foo.cpp
:
#include "foo.h"
void bar() {
// more complicated definition
}
main.cpp
:
#include "foo.h"
int main() {
foo();
bar();
}
Dans cet exemple, la fonction plus simple foo
est définie en ligne dans le fichier d'en-tête alors que la bar
fonctions plus compliquée n'est pas intégrée et est définie dans le fichier d'implémentation. Les deux unités de traduction foo.cpp
et main.cpp
contiennent des définitions de foo
, mais ce programme est bien formé puisque foo
est en ligne.
Une fonction définie dans une définition de classe (qui peut être une fonction membre ou une fonction amie) est implicitement intégrée. Par conséquent, si une classe est définie dans un en-tête, les fonctions membres de la classe peuvent être définies dans la définition de classe, même si les définitions peuvent être incluses dans plusieurs unités de traduction:
// in foo.h
class Foo {
void bar() { std::cout << "bar"; }
void baz();
};
// in foo.cpp
void Foo::baz() {
// definition
}
La fonction Foo::baz
est définie hors ligne, ce n'est donc pas une fonction inline et ne doit pas être définie dans l'en-tête.
Violation d'ODR via une résolution de surcharge
Même avec des jetons identiques pour les fonctions en ligne, les ODR peuvent être violés si la recherche de noms ne fait pas référence à la même entité. considérons func
en suivant:
header.h
void overloaded(int); inline void func() { overloaded('*'); }
foo.cpp
#include "header.h" void foo() { func(); // `overloaded` refers to `void overloaded(int)` }
bar.cpp
void overloaded(char); // can come from other include #include "header.h" void bar() { func(); // `overloaded` refers to `void overloaded(char)` }
Nous avons une violation de l'ODR comme overloaded
fait référence à différentes entités en fonction de l'unité de traduction.