What function does C++ write and call in an empty class?
Asked Answered
S

3

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?

Sulphurous answered 23/8, 2016 at 6:23 Comment(6)
Did you compile with optimizations?Eben
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: en.cppreference.com/w/cpp/language/…Parallelism
without any optimizationsSulphurous
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).Hudgens
Try declaring them volatileCensure
The book is talking about a notional compiler to help you understand what the code does. It's not talking about an actual compiler.Trucker
M
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.

Mortal answered 23/8, 2016 at 6:26 Comment(1)
Also you may want to read about as-if ruleGamete
S
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.

Saltant answered 23/8, 2016 at 6:27 Comment(0)
C
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.

Crossways answered 23/8, 2016 at 15:4 Comment(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.Cantonese
@JordanMelo: Glad you liked it - that's exactly what I was going for!Crossways

© 2022 - 2024 — McMap. All rights reserved.