📅 2019-Feb-21 ⬩ ✍️ Ashwin Nanjappa ⬩ 🏷️ cpp, inline, namespace ⬩ 📚 Archive
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.