С++ 11 переменная-член ссылочного типа, различное поведение после vector push_back
Я использовал какой-то другой класс, который работал нечетно, когда я ввел его в вектор. Он включает переменную-член, которая является ссылкой на другую переменную-член. Вот самый маленький самодостаточный пример:
#include <iostream>
#include <vector>
class Myclass {
public:
Myclass() : a(1.0) {}
float a;
float &a_ref = a;
void addOne() {
a = a + 1.0;
}
};
int main() {
Myclass instance1;
instance1.addOne();
//prints 2:
std::cout << "instance.a_ref is " << instance1.a_ref << std::endl;
std::vector<Myclass> vec;
Myclass instance2;
vec.push_back(instance2);
vec.at(0).addOne();
//prints 1;
std::cout << "vec.at(0).a_ref is " << vec.at(0).a_ref << std::endl;
return 0;
}
Я компилировал с g++
и -std=c++11
, поэтому я некоторое время не замечал проблему. Теперь я вижу, что проблема связана, вероятно, с синтезированным конструктором копии и ссылочным элементом. Но я не уверен в следующем:
- Почему существует другое поведение, когда объект находится в векторе?
- Почему
g++
не дает никаких предупреждений об этом, используя стандарт С++ 11?
Бонусный вопрос, потому что мне любопытно:
- Что инициализируется сначала,
a
или a_ref
?
Ответы
Ответ 1
Проблема действительно связана с конструктором копии по умолчанию. По умолчанию конструктор копирования инициализирует всех членов из элементов исходного объекта. То есть конструктор копии по умолчанию идентичен этому:
Myclass(const Myclass &src) :
a(src.a),
a_ref(src.a_ref)
{}
По умолчанию конструктор копирования инициализирует все члены, поэтому он игнорирует любые инициализаторы в классе.
Это также приводит к тому, что нажатие на вектор вызывает проблему. vec.at(0)
был создан как копия instance2
, что означает, что vec.at(0).a_ref
относится к instance2.a
. Вы можете легко проверить это, распечатав свои адреса (живой пример).
Ответ 2
Неявно определенный конструктор копирования/перемещения:
[...] выполняет a выполняет поэтапную копию/перемещение своих оснований и членов. [Примечание: скопированные или равные инициализаторы нестатических членов данных игнорируются. [...]
В частности, ссылочные элементы инициализируются напрямую, чтобы ссылаться на один и тот же объект, к которому относится соответствующий ссылочный элемент в исходном объекте.
Итак, в вашем случае vec.at(0).a_ref
относится к элементу a
instance2
.
Это не обнаружено компилятором, потому что, как ожидается, общие члены-члены ссылаются на объект с более длительным сроком жизни вне класса.