Неожиданный конструктор копирования - PullRequest
4 голосов
/ 24 марта 2020

В следующем примере я ожидаю только одну конструкцию копии, так как я думал, что промежуточные копии будут на исключать копию . Единственная необходимая (я думал?) Копия будет в конструкторе B для инициализации переменной-члена a.

#include <iostream>

struct A
{
    A() = default;
    A(A const&) { std::cout << "copying \n"; }
};

struct B
{
    B(A _a) : a(_a) {}
    A a;    
};

struct C : B
{
    C(A _a) : B(_a) {}
};

int main()
{
    A a{};
    C c(a);
}

Когда я выполню этот код -O3) Я вижу следующий вывод

copying 
copying 
copying 

Почему эти промежуточные копии не исключены?

Ответы [ 3 ]

6 голосов
/ 24 марта 2020

Вот случаи, когда допустимое значение копии разрешено (class.copy / 31):

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

  • в выражении броска, когда операндом является имя энергонезависимого объекта автомата c (другое чем параметр функции или предложения catch), область действия которого не выходит за пределы самого внутреннего охватывающего блока try (если он есть), операция копирования / перемещения из операнда в объект исключения (15.1) может быть опущена построение объекта c automati непосредственно в объекте исключения

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

Ничто из этого не верно для вашего примера (мы не находимся в выражении возврата, выражении броска или объявлении исключения. И в вашем примере вообще нет временных значений.), Поэтому копирование происходит каждый раз, когда вы ожидаете, что это произойдет.

Обратите внимание, что допустимое значение копирования разрешено в упомянутых случаях, но не обязательно , Таким образом, даже в этих случаях компилятору разрешено выдавать копии (это верно для C ++ 11. В C ++ 17 есть некоторые случаи, когда исключение копий является обязательным. Но ни один из ваших примеров не допускает elision в C ++ 17 тоже.)

1 голос
/ 24 марта 2020

Расширяя обсуждение под другим ответом, копия здесь имеет побочные эффекты (печать на консоль), и поэтому не подходит для оптимизации копирования.

Исключение копирования, однако, разрешено независимо от побочных эффектов В соответствии со статьей cppreference на копию elision. Это не разрешено здесь, потому что у вас есть все значения. Ни одна из копий, которые вы надеялись удалить, не имела значения или значения. Чтобы сделать их r-значением, вам нужно привести их с использованием std :: move или создать их как безымянные временные объекты как часть вызова конструктора.

Опять же, статья cppreference объясняет это гораздо лучше.

0 голосов
/ 24 марта 2020

Вы передаете объект конструктору C по значению. И затем он передается конструктору B по значению.

...