Какую функцию выполняет С++ для записи и вызова в пустом классе?

В книге "Эффективный С++" я увидел прохождение ниже:

В результате, если вы пишете

class Empty{};

он по существу такой же, как если бы вы написали это:

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

Следующий код вызовет создание каждой функции:

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

Но после разборки исполняемого файла, который был создан путем компиляции вышеприведенного кода, я понял, что это не так: никакой функции не вызывается.

Вот основной код сборки:

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 

В сегменте .text нет функции с именем "Пусто".

Тогда каково поведение компилятора после вызова конструктора или назначения пустого класса? Создает ли он некоторые функции, как говорила книга? Если да, то где они хранятся?

Ответ 1

Функции существуют, но могут быть встроены.

Когда компилятор строит функции, он понимает, что они не являются операциями, и не генерируется код.

То, что говорит книга, верно в той или иной степени, условные функции создаются компилятором для встроенного и прямого вызова.

Но сгенерированный код пуст, поэтому оптимизирующий компилятор затем удалит любые доказательства функции (настроив этот указатель), и функции никогда не будут вызваны напрямую.

Книга на самом деле не пытается объяснить генерируемый код, но влияет на создание класса и "скрытые" функции, которые он генерирует для нормальной работы.

Ответ 2

Эти методы действительно генерируются для класса, но они генерируются как "встроенные".

Поскольку они являются поэтапными реализациями (например, конструктор копирования будет копировать-строить все члены), когда class пуст, в действительности в них ничего не делается, а в строке они просто невидимы.

Очень важно помнить, однако, что эти методы автоматически реализуют реализацию... например, код

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

является ошибкой, потому что автоматически сгенерированный код для конструктора и назначения копирования неверен и приведет к множественному удалению буфера.

Это ошибка не из-за написанного, а для того, что написано не, и это сложно. Поэтому очень важно помнить, что С++ будет автоматически писать для вас: если эта реализация - это то, что вы хотите, то идеально, но если нет, то исправите ее, предоставив правильную реализацию или запретите создание или использование этого неправильного кода.

Ответ 3

Вы и книга приходят в эту ситуацию с разных уровней абстракции.

В книге используется термин "сгенерированный" для обозначения функций С++, которые неявно определяются компилятором в абстрактной программе на С++. Это абсолютно происходит.

Вы интерпретируете это, чтобы понимать фактическое генерирование фактического машинного кода в переведенной программе. Это не то, что это значит. Генерация фактического машинного кода всегда подчиняется прихотям компилятора, если сохраняется семантика исходной абстрактной программы.

Таким образом, книга, безусловно, неверна, хотя я, вероятно, использовал бы другое слово в интересах ясности. Придерживаться стандартной терминологии никогда не бывает больно.