Конструктор по умолчанию вызывается для константного ссылочного члена, несмотря на аргументы конструктора не по умолчанию

Вот некоторые основные наброски кода на C ++:

#include <cstdlib>
#include <iostream>
#include <thread>

using namespace std;

class M {
public:
M() = default;
~M() {
cout << "Called ~M" << endl;
}
};

class A {
public:
A(int z) : _z(z) {
cout << "Called A(int z)" << endl;
this_thread::sleep_for(chrono::milliseconds(1000));
}

A() {
cout << "Called A()" << endl;
this_thread::sleep_for(chrono::milliseconds(1000));
}
A(const A& a) {
cout << "Called A(const A& a)" << endl;
_z = a._z;
}
A(const A&& a) {
cout << "Called A(const A&& a)" << endl;
_z = a._z;
}
A& operator=(const A& a) {
cout << "Called A& operator=(const A& a)" << endl;
if (&a != this) {
cout << "Executed A& operator=(const A& a)" << endl;
}
}
virtual ~A() {
cout << "Called ~A" << endl;
}
int poll() const { return _z;  }

private:
M _m;
int _z = 300;
};

class B : public A {
public:
// _a(10)
B() : _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
virtual ~B() {
cout << "Called ~B" << endl;
}
private:
const A& _a;
};

int main(int argc, char** argv) {
B b;
A* aPtr = &b;
A& aRef = (*aPtr);
cout << aRef.poll() << endl;
return 0;
}

из настройки выше я получаю следующий вывод:

Called A()
Called A(int z)
Called ~A
Called ~M
Called B()
300
Called ~B
Called ~A
Called ~M

Моя проблема — первая строка вывода (все остальные имеют смысл, учитывая первую). Я инициализирую участника _a в B() : _a(std::move(A(10)))это принудительно _a является постоянным справочным членом. Также вызывается CTOR с аргументом int, однако почему CTOR по умолчанию вызывается для A? Почему нет движения CTOR? Поэтому временный объект просто кажется созданным и разрушенным, никакого реального движения не происходит (как можно увидеть из вывода 300 позже).

Теперь эта проблема, похоже, не связана с переездом как таковой но к поведению вокруг константного ссылочного члена. Потому что, если я изменю список инициализации на: B(): _a(10) Я получаю ту же проблему: каким-то образом объект по умолчанию назначается члену ссылки const, а аргументы в списке инициализации игнорируются. Таким образом, для B(): _a(10) Я получил:

Called A()
Called A(int z)
Called B()
300
Called ~B
Called ~A
Called ~M

В основном, почему первая строка является конструктором по умолчанию? И как мне изменить код так, чтобы вместо инициализации появилось 10 от инициализации вместо 300?

1

Решение

Каждый объект типа B на самом деле имеет два подобъекты типа А. Один — это подобъект базового класса, а другой — _a член подобъекта. Вы вызываете конструктор для члена, но подобъект базового класса инициализируется по умолчанию, поскольку вы явно не вызывали его конструктор в своем списке инициализации.

Вы можете сделать это, например, следующим образом:

B() : A(arguments) //<--initialize the base-class subobject
, _a(std::move(A(10))) {
cout << "Called B()" << endl;
}
3

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

Ваш B и то и другое содержит экземпляр A а также происходит от A (что, вероятно, ошибка).

Вы проходите 10 когда вы строите временный A объект, затем переместить это в член _a, Вы покидаете база подобъект класса по умолчанию инициализируется.

Чтобы это исправить, вам нужно включить базовый класс в список инициализатора члена:

B() : A(1010), _a(std::move(A(10))) {
cout << "Called B()" << endl;
}

Это инициализирует подобъект базового класса B с 1010 (чтобы отличить его от объекта-члена).

Если бы я собирался сделать это, я бы также инициализировал _a напрямую, поэтому ctor будет выглядеть примерно так:

B() : A(1010), _a(10) { // ...
2

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