Home
Fractals
Tutorials
Books
My blog
My LinkedIn Profile

BOOKS i'm reading

Napoleon Hill Keys to Success: The 17 Principles of Personal Achievement, Napoleon Hill, ISBN: 978-0452272811
The 4-Hour Workweek: Escape 9-5, Live Anywhere, and Join the New Rich (Expanded and Updated), Timothy Ferriss, ISBN: 978-0307465351
The Fountainhead, Ayn Rand, ISBN: 0452273331
Web Hosting Canada

mailto:olivier@olivierlanglois.net

Archives for: April 2008, 28

04/28/08

Permalink 09:55:06 pm, by lano1106, 680 words, 4656 views   English (CA)
Categories: C++

ODR, inline functions and C++ compiler generated functions

ODR is the One Definition Rule. This is the rule that stipulates that a name (a function, a class or global variable and much more other types) must be defined not more than once in the same program. The compiler will emit an error if a name is defined more than once in the same translation unit (TU) or if the name is defined more than once but in differents TUs, it is the linker that will catch the error and it will emit a duplicate symbol error.

The ODR is the reason why header files guards are used (#ifndef XXX #define XXX #endif). If a header file was not protected by guards and included more than once in a TU, the ODR would be violated.

All C++ programmers have a formal knowledge of the ODR or at least an intuition of its details learned by experience but this rule gets very interesting when you start to study the exceptions to it. Studying these things makes you more appreciate the complexity of the C++ language.

For instance, there are the inline functions. Usually, when we think about inline functions, we consider them as fancy macros with strong type checks. However the inline keyword can be considered only as a hint given to the compiler and the compiler is free to completely ignore the inline specifier. If it does ignore it, it will generate a regular function and export it from the resulting object file. If the inline functions are defined in many TUs and linked together then according to the ODR, this would be an error but inline functions are exempted from the ODR and the linker only keeps one copy of the inline functions defined multiple times. I would be very eager to figure out how the linker knows that a given function is exempted from the ODR because as you will see next, there are no apparent hints to this effect.

The next exception to the ODR is the C++ compiler generated functions for a class. Those functions include the copy constructor and the assignment operator. The C++ standard says that these functions are always implicitly declared if not explicitly done by the user but implicitly defined only if they are used. They will also be considered as inline hence also exempted from the ODR. Another property of the generated functions is that they are classified as trivial or non-trivial. Essentially, a trivial copy constructor simply means that the class looks more like a C POD (Plain Old Data) structure than a class and the compiler can use the same C compiler technique to perform the deep copy (probably something like a memcpy). Otherwise, it will need to create a real constructor function. Here is a simple example to demonstrate these notions:

C.h: 

class C 
{ 
public: 
   C() : a(0), b(0), c(0), d(0), e(0) {} 
//  virtual ~C() {} 
private: 
   int a; 
   int b; 
   int c; 
   int d; 
   int e; 
}; 

f.cpp: 

#include "C.h" 

C f() 
{ 
     C a; 
     C b = a; 
     return b; 
} 

g.cpp: 

#include "C.h" 

C g() 
{ 
     C a; 
     C b = a; 
     return b; 
} 

main.cpp: 

#include "C.h" 

C f(); 
C g(); 

int main(int argc, char *argv[]) 
{ 
   f(); 
   g(); 
   return 0; 
} 

g++ -c f.cpp 
nm -a -C f.o 

00000000 t 
00000000 d 
00000000 b 
00000000 t 
00000000 n 
00000000 n 
00000000 a f.cpp 
00000000 T f() 
00000000 W C::C() 

No sign of the copy constructor. I am guessing that the compiler make the copy constructor inline because it is trivial. I am adding a virtual destructor to make the copy constructor non-trivial:

00000000 T f() 
          U operator delete(void*) 
00000000 W C::C(C const&) 
00000000 W C::C() 
00000000 W C::~C() 
00000000 W C::~C() 
00000000 V typeinfo for C 
00000000 V typeinfo name for C 
00000000 V vtable for C 
          U vtable for __cxxabiv1::__class_type_info 

Now g.o and f.o have a copy of C copy constructor and the linker will link fine and only one copy of the function will find its way in the final executable file because the implicitly defined copy constructor is inline.

Olivier Langlois's blog

I want you to find in this blog informations about C++ programming that I had a hard time to find in the first place on the web.

April 2008
Sun Mon Tue Wed Thu Fri Sat
 << < Current> >>
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      

Search

Custom Search

Misc

XML Feeds

What is RSS?

Who's Online?

  • Guest Users: 3

powered by
b2evolution