Ru-Brd |
In practice we write C++ programs by filling files with "code." However, the boundary set by a file is not terribly important in the context of the ODR. Instead, what matters are so-called translation units . Essentially, a translation unit is the result of applying the preprocessor to a file you feed to your compiler. The preprocessor drops sections of code not selected by conditional compilation directives ( #if , #ifdef , and friends ), drops comments, inserts #include d files (recursively), and expands macros. Hence, as far as the ODR is concerned , having the following two files // File header.hpp: #ifdef DO_DEBUG #define debug(x) std::cout << x << '\n' #else #define debug(x) #endif void debug_init(); // File myprog.cpp: #include "header.hpp" int main() { debug_init(); debug("main()"); } is equivalent to the following single file: // File myprog.cpp: void debug_init(); int main() { debug_init(); } Connections across translation unit boundaries are established by having corresponding declarations with external linkage in two translation units (for example, two declarations of the global function debug_init() ) or by argument-dependent lookup during the instantation of export ed templates. Note that the concept of a translation unit is a little more abstract than just "a preprocessed file." For example, if we were to feed a preprocessed file twice to a compiler to form a single program, it would bring into the program two distinct translation units (there is no point in doing so, however). |
Ru-Brd |