Multiple definitions of inline function

In C++, a function is annotated with inline to implore the compiler to inline that function’s code into its callers. The C++ standard says that injecting multiple differing definitions of an inline function into a program is illegal because it breaks the ODR.

For example, ยง7.1.2 from the C++11 standard says:

An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case.

So, having multiple differing definitions is illegal but the compiler cannot really stop it. This is because two differing definitions of the same inline method might be inlined into their callers in two different compilation units that are linked together into a program.

Thus, if we use multiple different inline definitions, all bets are off and it is undefined behavior.

This example illustrates the final behavior differs based on just compilation flags:

// x.cpp
#include <iostream>

inline void f()
{
    std::cout << __FILE__ << std::endl;
}

extern void g();

int main()
{
    f();
    g();

    return 0;
}

// y.cpp
#include <iostream>

inline void f()
{
    std::cout << __FILE__ << std::endl;
}

void g()
{
    f();
}

$ g++ x.cpp y.cpp
$ ./a.out
x.cpp
x.cpp

$ g++ -O2 x.cpp y.cpp
$ ./a.out
x.cpp
y.cpp

If we want to control and want each caller to only use its local inline definition, then we can do that by placing the inline method inside an anonymous namespace. The practice of multiple differing inline definitions is still illegal, but at least we now have controlled what happens. This example illustrates this:

// x.cpp
#include <iostream>

namespace
{
inline void f()
{
    std::cout << __FILE__ << std::endl;
}
}

extern void g();

int main()
{
    f();
    g();

    return 0;
}
                                                                                                                                                                                // y.cpp
#include <iostream>

namespace
{
inline void f()
{
    std::cout << __FILE__ << std::endl;
}
}

void g()
{
    f();
}

$ g++ x.cpp y.cpp
$ ./a.out
x.cpp
y.cpp

$ g++ -O2 x.cpp y.cpp
$ ./a.out
x.cpp
y.cpp

Thanks to Arch for the elegant examples.

Advertisements

C++: Static Function in Header File

A colleague handed some C++ code to integrate which had static functions in its header file like this:

// Foo.h
static void FooFun()
{ /***/ }

In every .cpp file that includes Foo.h, a static FooFun() definition is compiled in. But, if you are compiling code at Warning Level 4 (/W4), then for every .cpp file where FooFun() is not invoked, the following warning is produced:

Main.cpp Foo.h(1): warning C4505: 'FooFun' : unreferenced local function has been removed

Due to some constraints, my colleague was not interested in moving the FooFun() definition into the Foo.cpp source file and exposing only the FooFun() declaration in Foo.h.

  • FooFun() cannot be defined as a normal non-static function in the header file Foo.h, since that would lead to linker errors that FooFun() is already defined in another .cpp file:
    Main.obj : error LNK2005: "void __cdecl FooFun(void)" (?FooFun@@YAXXZ) already defined in Joe.obj
    
  • The ideal way to handle this situation is to encapsulate FooFun() as a static function in a class:
    // Foo.h
    class Foo
    {
    public:
        static void FooFun()
        { /***/ }
    };
    

    The header file can be included in all .cpp files with no problems. The function can be invoked as Foo::FooFun() in other .cpp files and there is no compiler warning.

  • If for some reason this is not suitable, then the only other solution is to make the function as static inline:
    // Foo.h
    static inline void FooFun()
    { /***/ }
    

    Inclusion of this function definition causes no problems, and invocation of the function causes it to be inlined, thus leaving no symbols behind for linker errors. Note however, that it comes with the baggage of inline, so it should be used only for tiny function bodies.