diff options
Diffstat (limited to '_posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md')
-rw-r--r-- | _posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md | 279 |
1 files changed, 0 insertions, 279 deletions
diff --git a/_posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md b/_posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md deleted file mode 100644 index 4cb0960..0000000 --- a/_posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md +++ /dev/null @@ -1,279 +0,0 @@ ---- -title: static vs. inline vs. namespace { -excerpt: > - Should I use `static`, `inline` or unnamed namespaces for function - definitions? -category: C++ -custom_css: - - snippets.css - - syntax.css -snippets_root_directory: snippets/static_vs_inline_vs_unnamed_namespaces -snippets_language: c++ -snippets: - static: - - static/main.cpp - - static/proxy.cpp - - static/proxy.hpp - - static/shared.hpp - inline: - - inline/shared.hpp - inline_weird: - - inline/weird/main.cpp - - inline/weird/another.cpp - - inline/weird/another.hpp - unnamed_namespaces_weird: - - unnamed_namespaces/weird/main.cpp - - unnamed_namespaces/weird/another.cpp - - unnamed_namespaces/weird/another.hpp - unnamed_namespaces_ok: - - unnamed_namespaces/ok/main.cpp - - unnamed_namespaces/ok/another.cpp - - unnamed_namespaces/ok/another.hpp - static_and_inline: - - static_and_inline/main.cpp - - static_and_inline/proxy.cpp - - static_and_inline/proxy.hpp - - static_and_inline/shared.hpp - unnamed_namespace_and_inline: - - unnamed_namespace_and_inline/main.cpp - - unnamed_namespace_and_inline/proxy.cpp - - unnamed_namespace_and_inline/proxy.hpp - - unnamed_namespace_and_inline/shared.hpp - separate_method_definitions: - - separate_method_definitions/main.cpp - - separate_method_definitions/another.cpp - - separate_method_definitions/another.hpp - - separate_method_definitions/shared.hpp ---- -In this post I'll try to figure out whether I should use `static`, `inline` or -unnamed namespaces for function definitions. - -`static` --------- - -It's an old C-style method of defining functions in header files. -This way, every translation unit gets its own copy of a function. -What does that mean? -The most obvious implication that pops into my head is that every local static -variable defined inside that function gets an independent replica in every -translation unit. -For example, the program below would print - -``` -1 -1 -``` - -due to the fact that both main.cpp and proxy.cpp get their own versions of `n` -from `shared()`. - -{% include snippets/section.html section_id='static' %} - -In C, this is the only way to share function definitions between translation -units (apart from the usual way of declaring a function in a header file and -putting its definition to a .c file). - -### Properties - -* Using `static`, you can share function definitions between multiple -translation units. -* Each unit gets its own replica of the function: they have different -addresses, their local static variables are independent, etc. -* If different translation units define different functions with the same -name using the `static` specifier, each unit can use its function without any -issues. -This might seem like an trivial claim, but other approaches sometimes disallow -this, which is discussed below. - -`inline` --------- - -It's well-known that this keyword has pretty much nothing to do with whether a -function will actually be inlined or not. -It's used much more often to define functions in header files, since every -function defined this way will be the same (as in "will have the same address") -in every translation unit. -Let's try and adjust the definition of `shared()` accordingly: - -{% include snippets/section.html section_id='inline' %} - -The same program would then print - -``` -1 -2 -``` - -since both `main()` and `proxy()` would call the same `shared()`, incrementing -the same `n`. - -Weird things happen when different translation units define different `inline` -functions with the same name. - -{% include snippets/section.html section_id='inline_weird' %} - -According to my simple experiments, this program produces different output -based on which .cpp file was specified first on the command line during -compilation. -For example, this is the output of test.exe produced with either `cl /W4 /EHsc -main.cpp another.cpp /Fe:test.exe` or `g++ -Wall -Wextra -std=c++11 main.cpp -another.cpp -o test.exe`. - -``` -main.cpp: shared() -main.cpp: shared() -``` - -If we swap the order of .cpp files (`another.cpp main.cpp` instead of `main.cpp -another.cpp`), the output becomes - -``` -another.cpp: shared() -another.cpp: shared() -``` - -No warnings/errors are emitted, making the situation truly disturbing. -I tested this with GNU compiler version 5.4.0 and Microsoft compiler version -19.00.24210. - -This behaviour can be easily fixed either by making these functions `static` or -by using unnamed namespaces (see below). - -### Properties - -* Using `inline`, you can share function definitions between multiple -translation units. -* Each translation unit will use the same function: it will have the same -address in every translation unit, its local static variables will be shared, -etc. -* Defining different `inline` functions with the same name in different -translation units is undefined behaviour. - -Two inline functions might be different even if they are the same textually. -For example, they might reference two global variables which have the same -name, but are defined in different translation units. -{: .alert .alert-info } - -`namespace {` -------------- - -With respect to function definitions, unnamed namespaces are, according to my -understanding, quite similar to the `static` keyword. -The additional value they provide is that they provide a way to apply `static` -not only to functions, but to classes also. -Remember the weirdness that happens when multiple translation units define -different `inline` functions with the same name? -Arguably, it gets even worse if we add classes to the equation. - -{% include snippets/section.html section_id='unnamed_namespaces_weird' %} - -Compiling this program the same way we did in the `inline` example (`cl /W4 -/EHsc main.cpp another.cpp /Fe:test.exe`/`g++ -Wall -Wextra -std=c++11 main.cpp -another.cpp -o test.exe`) yields different outputs depending on which .cpp file -was specified first. - -``` -main.cpp: Test::Test() -1 -main.cpp: Test::Test() -``` - -``` -another.cpp: Test::Test() -1065353216 -another.cpp: Test::Test() -``` - -I'm not sure why anybody would want that. -This can be easily fixed by putting both `Test` classes into unnamed -namespaces. -The program than reads - -{% include snippets/section.html section_id='unnamed_namespaces_ok' %} - -After the adjustment, it produces the same output regardless of compilation -options. - -``` -main.cpp: Test::Test() -1 -another.cpp: Test::Test() -``` - -Notice how sharing classes defined in header files isn't discussed here. -The standard actually guarantees that if a class is defined in a header file, -all translation units that use it share the definition. - -### Properties - -* Essentially, unnamed namespaces allow the `static` keyword to be applied to -classes. -* Similar to the `static` approach, each translation unit gets its own replica -of a function/class, including their own local static variables, etc. -* Defining different classes with the same name in different translation units -(without utilizing unnamed namespaces) is undefined behaviour. - -Conclusion ----------- - -Here's my attempt to build an algorithm to decide whether a class/function -should be defined with either of the `static`/`inline` specifiers or put into -an unnamed namespace. -The first question I answer is: is the entity defined in a header file or in a -.cpp file? - -* **In a header** — Is it a class or a function? - * **Class** — There's no need to do anything. - * **Function** — Do you want it to behave differently for each -translation unit (may be useful, for example, for logging)? - * **Yes** — Use `static`. - * **No** — Use `inline`. -* **In a .cpp file** — Put it into an unnamed namespace. - -Tricky cases ------------- - -### `static` + `inline` - -In case a function is defined as `static inline`, `static` wins, and `inline` -is ignored. -The program below outputs - -``` -1 -1 -``` - -{% include snippets/section.html section_id='static_and_inline' %} - -In general, I can't think of a reason to define a `static inline` function. - -### `namespace {` + `inline` - -If an `inline` function is defined in an unnamed namespace, the unnamed -namespace wins. -The program below outputs - -``` -1 -1 -``` - -{% include snippets/section.html section_id='unnamed_namespace_and_inline' %} - -In general, I can't think of a reason to define an `inline` function in an -unnamed namespace. - -### Separate method definitions - -If you want to separate your class declaration from its method definitions -while keeping them in the same header file, each method must be explicitly -defined `inline`. -The program below outputs - -``` -1 -2 -``` - -{% include snippets/section.html section_id='separate_method_definitions' %} |