Search code examples
c++functionglobalheader-files

C++ Types of global header functions explained


Currently I want to have a global function inside a header file. I've found three possible ways to implement it (there are probably more).
The first one is to have a class which has a public static function:

class foo {
public:
    static void bar() {
        ...
    }
};

The second is to have an inline global function:

inline void bar() {
    ...
}

And the third is to use C style static header functions:

static void bar() {
    ...
}

I was wondering if there is a universally accepted correct way of having global functions inside header files and what are the advantages and disadvantages of the above methods.


Solution

  • An alternative to the first approach (a static member function in a class) is to use a named namespace:

    namespace foo {
        inline void bar () {
            ...
        }
    }
    

    If every member of the class foo is static, it might well be better to use the named namespace approach as the latter (namespace foo) does a better job of showing intent.

    An alternative to the last approach (using C-style static functions) is to use an anonymous namespace:

    namespace {
        void bar () {
            ...
        }
    }
    

    Which of these two approaches (C-style static functions versus anonymous namespace) is better is a matter of opinion. The use of static at namespace scope, including the global namespace, was deprecated until C++11.

    So now we have five approaches, as opposed to the three listed in the question.


    Making the functions inline (your first two options, plus my named namespace approach) means that there will be at most one definition of the function in the executable. The inline keyword is a weak hint to the implementation that it should consider expanding the function in question in-line in places where the function is invoked. On the other hand, the inline keyword is a very strong command to the implementation regarding the one definition rule. What inline means in this context is that it is a promise to the implementation that while there might be multiple definitions in different translation units, each one of those definitions will be exact duplicates of one another.

    If you are using a compiler and a linker, the compiler may decline to obey your inline hint and instead define the function out-of-line. This out-of-line definition will be visible to the linker. The linker will cull the multiple definitions in different translation units down to just one definition in the generated executable.

    Suppose your function definition involves macros, the expansion of which differs from one translation unit to the next. Or suppose you have compiled different translation units that use the same function at different optimization levels, making the compiled version of the function different in different translation units. Either way, you have broken your promise to the implementation that the multiple definitions of the function across translation units are indeed identical. The implementation is not required to check for this, "no diagnostic required". The implementation will arbitrarily choose one of those definitions as the one definition to be used across the executable.


    Making the function static (either explicitly via your third option, or implicitly via my anonymous namespace option) hides the definition from the linker. The executable will have multiple implementations of what is essentially the same function if you use that function in several translation units.

    Using the internal linkage approaches (C-style static function or anonymous namespace) avoids the above issues because here the One Definition Rule applies at the translation unit level rather than the executable level. This approach can be useful for ensuring that one translation unit uses a highly optimized version of a function while another translation unit uses a non-optimized, debug-friendly version of the same function. The downside is a bloated executable if all definitions are indeed identical.