Конструктор копирования не вызывается при инициализации объекта с возвращаемым значением функции

Рассмотрим следующий код:

#include <iostream>

using namespace std;

class A
{
public:
int a;
A(): a(5)
{
cout << "Constructor\n";
}
A(const A &b)
{
a = b.a;
cout << "Copy Constructor\n";
}
A fun(A a)
{
return a;
}
};

int main()
{
A a, c;
A b = a.fun(c);
return 0;
}

Вывод вышеуказанного кода с g++ file.cpp является:

Constructor
Constructor
Copy Constructor
Copy Constructor

Вывод вышеуказанного кода с g++ -fno-elide-constructors file.cpp является:

Constructor
Constructor
Copy Constructor
Copy Constructor
Copy Constructor

Я знаю, Оптимизация возврата Мой вопрос в том, какой вызов конструктора копирования исключен (временный объект во время возврата или возвращаемый объект копируется в b)?

Если конструктор с удаленной копией используется для создания b, то как вообще создается b (поскольку в этом случае также нет вызова конструктора)?

Если я заменю линию A b = a.fun(c); с a.fun(c) и скомпилировать с использованием первого или даже второго метода, затем также вызывается конструктор копирования 2 раза. Итак, если в случае, описанном в предыдущем абзаце, конструктор копирования временного объекта исключается, то почему он не исключается в этом случае?

9

Решение

#include <iostream>

using namespace std;

class A
{
public:
int a;
A(): a(5)
{
cout << "Constructing: " << (void *)this << std::endl;
}
A(const A &b)
{
a = b.a;
cout << "Copy Constructor: " << (void *)this << " from " << (void *)&b << std::endl;
}
A fun(A a)
{
return a;
}
};

int main()
{

A a, c;
A b = a.fun(c);

std::cout << "a:" << (void *)&a << std::endl <<
"b:" << (void *)&b << std::endl <<
"c:" << (void *)&c << std::endl;
return 0;
}

Урожайность:

Constructing: 0x7fffbb377220
Constructing: 0x7fffbb377210
Copy Constructor: 0x7fffbb377230 from 0x7fffbb377210
Copy Constructor: 0x7fffbb377200 from 0x7fffbb377230
a:0x7fffbb377220
b:0x7fffbb377200
c:0x7fffbb377210

Так он конструирует a, конструкции cкопии c на промежуточный (аргумент a функции), а затем копирует промежуточный продукт непосредственно в b, пропуская типичное копирование в возвращаемое промежуточное звено. Это даже лучше продемонстрировано, если вы передадите по значению (измените на A fun(const A& a):

Constructing: 0x7fff8e9642b0
Constructing: 0x7fff8e9642a0
Copy Constructor: 0x7fff8e964290 from 0x7fff8e9642a0
a:0x7fff8e9642b0
b:0x7fff8e964290
c:0x7fff8e9642a0

a создается, c создается, c копируется непосредственно в b, несмотря на то, что b не передается в fun!

6

Другие решения

Исключенная копия является копией временного возвращаемого значения в b, Без исключения возвращаемое значение инициализируется из a и скопированы в b, Вместо этого временное, которое иначе содержало бы возвращаемое значение построен в b и инициализируется с a. [Class.copy] / 31:

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

Вы можете наблюдать это, если добавите дополнительный вывод в fun:

A fun(A a)
{
cout << "fun!" << endl;
return a;
}

Тогда с elision вы получите

[…] веселье!
Копировать конструктор

И без:

[…] веселье!
Копировать конструктор
Копировать конструктор

4

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector