Рассмотрим следующий код:
#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 раза. Итак, если в случае, описанном в предыдущем абзаце, конструктор копирования временного объекта исключается, то почему он не исключается в этом случае?
#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!
Исключенная копия является копией временного возвращаемого значения в b
, Без исключения возвращаемое значение инициализируется из a
и скопированы в b
, Вместо этого временное, которое иначе содержало бы возвращаемое значение построен в b
и инициализируется с a
. [Class.copy] / 31:
когда временный объект класса, который не был связан со ссылкой
(12.2) будет скопирован / перемещен в объект класса с тем же
cv-unqualified type, операция копирования / перемещения может быть опущена
строительство временного объекта непосредственно в цель
опущено копирование / перемещение
Вы можете наблюдать это, если добавите дополнительный вывод в fun
:
A fun(A a)
{
cout << "fun!" << endl;
return a;
}
Тогда с elision вы получите
[…] веселье!
Копировать конструктор
И без:
[…] веселье!
Копировать конструктор
Копировать конструктор