#cpp
Всё глубже и глубже погружаясь в C++, я начинаю немного сходить с ума, виной этому то, что некоторые вещи я просто не могу объяснить, а заучивать отдельные случаи как-то глупо. Сейчас на моём пути конструктор копий... Сразу оговорюсь, что я использую C++ 17. Так вот, возьмём этот код class A { public: A() { cout << "construct\n"; } ~A() { cout << "destruct\n"; } A(const A &obj) { cout << "copy\n"; } }; A f() { A a; cout << &a << "\n"; return a; } int main() { A a(f()); cout << &a << "\n"; } Результат его выполнения construct 0x7ffdca371107 0x7ffdca371107 destruct Честно говоря я без понятия почему результат такой. По идее при возвращении из функции должна создаваться копия объекта, затем при инициализации переменной в main'e должен вызываться конструктор копии этой копии объекта, в результате должны получить новый объект с новым адресом, но бит в бит такой же как его копия. Но в результате всё не так, при этом абсолютно не так. А теперь ещё для кого-то сюрприз, для кого-то нет: закомментируем обязательно и деструктор, и конструктор копии, если что-то из них останется, то это не прокатит. Теперь результат такой construct 0x7fff090c99f7 0x7fff090c9a27 А вот это уже больше походит на то, что я написал ранее. Но всё же я не уверен, что полностью, т.к. я не могу использовать конструктор копии и деструктор, чтобы убедиться, не изменяя поведение кода. Конкретно меня интересует 2 раза ли выполнится код деструктора при инициализации переменной a в main'e. По идее 1 раз должен вызваться при завершении функции f(), а 2 после того, как инициализируется переменная a. Меня интересуют ответы на поставленные вопросы, а также логика, почему всё-таки всё работает так, а не иначе.
Ответы
Ответ 1
В данном случае имеют место две оптимизации: Named Returned Value Optimization - при возврате локальной переменной a, чья область видимости тут же оканчивается, компилятор ограничивается созданием только одной переменной - возвращаемым значением. Эта оптимизация не гарантирована, хотя почти всегда выполняется. Temporary materialization - rvalue, возвращаемое функцией f, материализуется сразу в переменную а без создания временного объекта. В С++17 компилятор обязан откладывать материализацию временных переменных как можно дальше, устраняя все избыточные промежуточные объекты. Так что цепочки вида A a{A{A{A{A{}}}}}; приводят к появлению только одного объекта, а не целой пачки, и не содержат вызовов копирующих или перемещающих конструкторов (которых может вообще не быть). Таким образом, в функции main выделяется место только под один объект, который инициализируется в функции f минуя все промежуточные временные объекты. Причем компилятор пропускает вызовы конструкторов и деструкторов с побочными эффектами.
Комментариев нет:
Отправить комментарий