Почему конструктор копирования не вызывается?

В этом коде:

#include <iostream>

using std::cout;

class Foo {
    public:
        Foo(): egg(0) {}
        Foo(const Foo& other): egg(1) {}

        int egg;
};

Foo bar() {
    Foo baz;
    baz.egg = 3;
    return baz;
}

int main(void) {
    Foo spam(bar());
    cout << spam.egg;
    return 0;
}

вывод 3, тогда как я ожидал, что оно будет 1.

Это означает, что конструктор копирования не вызывается в строке Foo spam(bar()).

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

Не могли бы вы объяснить, что действительно происходит при инициализации spam?

Я извиняюсь заранее, если это тупой вопрос.

Спасибо!

Ответ 1

Копирование/перемещение elision является единственным разрешенным исключением из так называемого правила as-if, которое обычно ограничивает виды преобразований (например, оптимизаций), которые компилятор может выполнять в программе.

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

В абзаце 12.8/31 стандарта С++ 11:

При выполнении определенных критериев реализация допускает опустить конструкцию копирования/перемещения класса object, , даже если конструктор, выбранный для операции копирования/перемещения и/или деструктор для объекта имеют побочные эффекты. [...] Это разрешение операций копирования/перемещения, называемое копированием, разрешено в следующих обстоятельствах ( могут быть объединены для устранения нескольких копий):

  • в выражении return в функции с возвращаемым типом класса, когда выражение является именем энергонезависимый автоматический объект (кроме функции или параметра catch-clause) с тем же самым cv-unqualified тип как возвращаемый тип функции, операцию копирования/перемещения можно опустить при построении автоматический объект непосредственно в функции возвращает значение

[...]

  • когда временный объект класса, который не был привязан к ссылке (12.2), будет скопирован/перемещен к объекту класса с тем же cv-неквалифицированным типом операция копирования/перемещения может быть опущена построение временного объекта непосредственно в цель пропущенной копии/перемещения

[...]

Другими словами, вы никогда не должны полагаться на конструктор копирования или перемещать конструктор, вызываемый или не вызываемый в случаях, для которых применяются положения 12.8/31.