Ни конструктор по умолчанию, ни конструктор копирования не вызываются при использовании анонимного объекта

#c #copy-constructor #default-constructor #move-constructor #most-vexing-parse

#c #copy-constructor #default-constructor #переместить-конструктор #most-vexing-parse

Вопрос:

Ниже приведен фрагмент, предназначенный для тестирования конструкторов. Он был запущен в VS 2015.

На мой взгляд, «B b (B ())» имеет ту же функцию, что и «B b = B ()», однако мой код, похоже, говорит, что они ведут себя по-разному.

Я знаю, что оптимизация компилятора исключает копирование, но я думаю, что конструктор по умолчанию должен вызываться по крайней мере при выполнении «B b (B ())». Кто-нибудь может помочь указать, где я неправильно понял?

 class B
{
public:
    B() {
          i;
        x = new int[100];
        cout << "default constructor!"<<" "<<i << endl;
        cout << "x address:" << x << endl << "--------" << endl;
    }
    B(const B amp;b)  //copy constructor
    {   
          i;
        cout << "Copy constructor amp; called " << i<< endl 
            << "--------" << endl;
    }
    B(B amp;amp;b)//move constructor
    {
        x = b.x;    
          i;
        b.x = nullptr;
        cout << "Copy constructor amp;amp; called" << i << endl 
            <<"x address:"<< x << endl << "--------" << endl;

    }
    void print()
    {
        cout << "b address:" << x << endl << "--------" << endl;
    }
private:
    static int i;
    int *x;
};
int B::i = 0;


int main()
{
    B b1; //default constructor
    b1.print();
    B b2 = B(); //default constructor only
    b2.print();
    B b3(B());  //????nothing called... why???
    B b4 = b2; //copy constructor
    B b5 = move(b2); //move constructor
    b2.print();

    return 0;
}
  

введите описание изображения здесь

Ответ №1:

Обратите внимание, что B b(B()) это объявление функции, а не определение переменной вообще, тогда конструктор не будет вызван.

Согласно наиболее неприятному синтаксическому анализу, B b(B()) это объявление функции для функции с именем b , которая возвращает объект типа B и имеет один (неназванный) параметр, который является указателем на функцию, возвращающую тип B и не принимающую параметр.

Вы можете решить это с помощью фигурных скобок (инициализация списка (начиная с C 11)), например

 B b1( B{} );
B b2{ B() };
B b3{ B{} };  // preferable from C  11
  

Комментарии:

1. Хороший улов! @Shirley Feng, согласно статье в вики, вы можете получить ожидаемое поведение, добавив круглые скобки вокруг вашего внутреннего вызова ctor . Пример: B b3( (B()) );

2. Спасибо за сообщение!! Это спасает меня XD

3. Просто интересно, не лучше ли продолжать использовать одно соглашение для инициализации? т. е. B b{ B{} }; вместо

4. @AdrianShum Вы правы. Я просто хочу отметить, что только использование одной пары фигурных скобок разрешило бы двусмысленность здесь.

5. @ShirleyFeng Да! Это действительно важная функция C 11.