Visual C ++: копирование по значению в функцию params создает два объекта в vs2012

Вот код, который производит различный вывод в g ++ 4.7 и vs2012 (cl17).

#include <iostream>

using namespace std;

class A
{
public:
A() { cout << "1" << endl; }
~A() { cout << "2" << endl; }
};

class B : public A
{
public:
B() { cout << "3" << endl; }
~B() { cout << "4" << endl; }
};

void func(A a) {}

int main()
{
B b;
func(b);
return 0;
}

Выход GCC 13242пока cl выводит 132242,

Почему компилятор cl производит второй A объект, пока он делает копию в стеке, и для какой цели?

23

Решение

Кажется, это ошибка компилятора.
Стандарт C ++ не использует термин Нарезка объектов, Вы передаете объект типа B к функции, которая получает параметр типа A, Компилятор применит обычное разрешение перегрузки, чтобы найти соответствующее совпадение. В этом случае:
Базовый класс A имеет предоставленный компилятором конструктор копирования, который будет принимать ссылку на A и в отсутствие других функций преобразования это лучшее совпадение и должно использоваться компилятором.

Обратите внимание, что если бы было доступно лучшее преобразование, оно использовалось бы. Например: если A был конструктор A::A( B const& ), в дополнение к конструктору копирования, вместо конструктора копирования будет использоваться этот конструктор.

5

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

Компилятор C ++ будет синтезировать конструктор копирования по умолчанию в следующей ситуации. (Изнутри объектной модели C ++)

  1. Когда класс содержит объект-член класса, для которого существует конструктор копирования.
  2. Когда класс является производным от базового класса, для которого существует конструктор копирования.
  3. Когда класс объявляет одну или несколько виртуальных функций
  4. Когда класс является производным от цепочки наследования, в которой один или несколько базовых классов являются виртуальными.

Мы видим, что класс А не в 4 ситуациях. Так что cl НЕ синтезирует конструктор копирования по умолчанию для него. Может быть, поэтому 2 временных объекта А построены и уничтожены.

Из окна дизассемблирования, мы видим следующий код, без вызова A :: A. :

B b;
00B317F8  lea         ecx,[b]
00B317FB  call        B::B (0B31650h)
00B31800  mov         dword ptr [ebp-4],0
func(b);
00B31807  mov         al,byte ptr [ebp-12h]
00B3180A  mov         byte ptr [ebp-13h],al
00B3180D  mov         byte ptr [ebp-4],1
00B31811  movzx       ecx,byte ptr [ebp-13h]
00B31815  push        ecx
00B31816  call        func (0B31730h)

Но если мы сделаем деструктор виртуальным. Мы получим следующий код дизассемблирования, мы увидим, что A :: A вызывается. Тогда результат, как и ожидалось, только 1 объект создан.

B b;
00331898  lea         ecx,[b]
0033189B  call        B::B (03316A0h)
003318A0  mov         dword ptr [ebp-4],0
func(b);
003318A7  push        ecx
003318A8  mov         ecx,esp
003318AA  mov         dword ptr [ebp-1Ch],esp
003318AD  lea         eax,[b]
003318B0  push        eax
003318B1  call        A::A (0331900h)
003318B6  mov         dword ptr [ebp-20h],eax
003318B9  call        func (03317D0h)
0

Вы столкнулись с ошибкой компилятора.

Надлежащая функциональность объясняется ниже:


Функция func Нужно создать копию объекта (но будьте осторожны с нарезкой).

Итак, что происходит, это:

int main()
{
// create object B, which first creates the base object A
B b;
// create object A, using this copy constructor : A( const B& )
func(b);
}

Дополнительный вызов ~ A () выполняется, когда созданный копией объект A уничтожается в конце func вызов.

-3
По вопросам рекламы [email protected]