44

In the book Effective C++, I saw the passage below:

As a result, if you write

class Empty{};

it's essentially the same as if you'd written this:

class Empty {
public:
    Empty() { ... }
    Empty(const Empty& rhs) { ... }
    ~Empty() { ... }
    Empty& operator=(const Empty& rhs) { ... } // copy assignment operator
};

The following code will cause each function to be generated:

Empty e1;
Empty e2(e1);
e2 = e1;

But after disassembling the executable file which was created by compiling the code above, I realized it not the case: there isn't any function being invoked.

Here is the major assembly code:

00000000004006cd <main>:
  4006cd:       55                      push   %rbp
  4006ce:       48 89 e5                mov    %rsp,%rbp
  4006d1:       b8 00 00 00 00          mov    $0x0,%eax
  4006d6:       5d                      pop    %rbp
  4006d7:       c3                      retq 

There isn't any function named "Empty" in the .text segment.

Then what indeed is the behaviour of a compiler after we call a constructor or assignment of an empty class? Does it generate some functions as the book said? If so, where are they stored?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
llinvokerl
  • 1,029
  • 10
  • 25
  • 12
    Did you compile with optimizations? – Rakete1111 Aug 23 '16 at 06:25
  • 8
    To be pedantic, `Empty() { ... }` is *not quite* equivalent to what the compiler generates, to get the same as what the compiler generates you'd need `Empty() = default;`. There are subtle differences - the same goes for the other members. See here: http://en.cppreference.com/w/cpp/language/default_constructor#Trivial_default_constructor – Jesper Juhl Aug 23 '16 at 06:32
  • without any optimizations – llinvokerl Aug 23 '16 at 06:37
  • 7
    Even without optimization the compiler will not generate such kind of no-op class, try adding a single object (of non-primitive type) inside the struct and you'll see that these members are generated (e.g. add a `std::string`). – Holt Aug 23 '16 at 06:39
  • 1
    Try declaring them `volatile` – adnan_e Aug 23 '16 at 07:03
  • 2
    The book is talking about a notional compiler to help you understand what the code does. It's not talking about an actual compiler. – David Schwartz Aug 24 '16 at 04:10

3 Answers3

55

The functions exist, but can be inlined.

When the compiler inlines the functions, it realizes they are no-ops, and there is no code generated.

What the book is saying, is true to an extent, the notional functions are created by the compiler, for inline and direct calling.

But the generated code is empty, so an optimizing compiler will then remove any evidence of the function (setting up a this pointer), and the functions will never be directly called.

The book is not really trying to explain the code generated, but the impact of creating a class, and the "hidden" functions it generates for normal operation.

mksteve
  • 12,614
  • 3
  • 28
  • 50
  • 17
    Also you may want to read about [as-if rule](http://stackoverflow.com/questions/15718262/what-exactly-is-the-as-if-rule) – MatthewRock Aug 23 '16 at 10:40
21

Those methods are indeed generated for the class, but they are generated as "inline".

As they are member-by-member implementations (for example the copy constructor will copy-construct all members) when the class is empty then nothing is actually done in them, and being inline they're just invisible.

It's very important to remember however that those methods get automatically an implementation... for example the code

struct Foo {
    char *buf;
    Foo() : buf(new char[10]) {}
    ~Foo() { delete[] buf; }
};

is buggy, because the automatically generated code for copy constructor and assignment is wrong and will lead to multiple deletion of the buffer.

It is buggy not because of something that has been written, but for something that has not been written and this is tricky. That's why is extremely important to remember what C++ will write automatically for you: if that implementation is what you want then perfect, but if not then fix it by providing the correct implementation or forbid the creation or use of that wrong code.

6502
  • 112,025
  • 15
  • 165
  • 265
9

You and the book are coming at this situation from different levels of abstraction.

The book uses the term "generated" to refer to C++ functions being implicitly defined by the compiler into the abstract C++ program. This absolutely does happen.

You're interpreting it to mean actual generation of actual machine code in the translated program. That's not what it means. Generation of actual machine code is always subject to the compiler's whims, as long as the semantics of your original abstract program are maintained.

As such, the book is certainly not incorrect, though I would probably have used a different word in the interests of clarity. Sticking to standard terminology never hurts.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 2
    This is my favourite answer because it's more general. Inlined functions or not, the compiler can output nothing because the program does nothing. The distinction between the abstract C++ program and the concrete machine code implementation of it is an important one. – Jordan Melo Aug 25 '16 at 19:47
  • @JordanMelo: Glad you liked it - that's exactly what I was going for! – Lightness Races in Orbit Aug 25 '16 at 22:29