Оператор присваивания и конструктор копирования при наличии ссылок
Я просто экспериментирую со ссылками, используя этот код:
class A
{
};
class B
{
public:
B(A& a): m_a(a){}
A& m_a;
};
int main()
{
A a;
B b(a);
B b1 = b;
}
Я ожидал, что оба B b1 = b;
приведут к ошибке. Вместо этого, когда я компилирую с VS2008, я просто получаю предупреждение
Я понимаю, почему получаю это предупреждение. Но не должен ли компилятор генерировать ошибку и для оператораПредупреждение C4512:' B': назначение оператор не может быть сгенерирован
B b1 = b;
? Это похоже на то, как он сгенерировал конструктор копирования, но не сгенерировал оператор присваивания. Разве они не связаны друг с другом по своей сути ? имеет ли смысл создавать реализацию по умолчанию только для одного из них, когда другой не может быть создан?5 ответов:
warning C4512: 'B' : assignment operator could not be generated
Вопрос 1: Почему это предупреждение?
ссылки может быть инициализирован только один раз при их создании. Вы не можете переназначить ссылку на другую переменную того же типа после создания, потому что ссылка является просто псевдонимом переменной типа, для которой она была создана и будет оставаться таковой. Попытка переназначить его приводит к ошибке.
Обычно компилятор по умолчанию генерирует неявный битовый оператор присваивания для каждого класса но в этом случае, посколькуclass B
имеет ссылку в качестве членаm_a
, если компилятор должен был бы генерировать неявный оператор присваивания, это нарушило бы фундаментальное правило, что ссылки не могут быть переназначены. Таким образом, компилятор генерирует это предупреждение, чтобы сообщить вам, что он не смог создать неявный оператор присваивания.Вопрос 2: но разве компилятор не должен генерировать ошибку и для оператора B b1 = b;?
Сгенерированное предупреждение и это конкретные операции не имеют никакого отношения вообще.B b1 = b;
вызывает неявный (как справедливо указал @AndreyT) конструктор копированияB::B(const B&)
. Неявный конструктор копирования - это одна из функций-членов, которую класс создает по умолчанию. Так что нет никакого предупреждения или ошибки для него.Вопрос 3: это похоже на то, что он сгенерировал конструктор копирования, но не сгенерировал оператор присваивания. Разве они не связаны друг с другом по своей сути ?
Нет, они вовсе не родственники. Да компилятор создал конструктор копирования, но не смог создать оператор присваивания по причине, указанной в ответе на вопрос 1 выше. Это происходит потому, что ссылка на элементm_a
может быть инициализирована в теле самого конструктора. это просто начальное назначение в момент создания, а не назначение, как в случае=
.Вопрос 4: имеет ли смысл генерировать реализацию по умолчанию только для одного из них, когда другой не может быть сгенерировано?
Ответ на вопрос 3, кажется, отвечает на это.просто чтобы повторить операции, выполняемые в вашем примере кода:
B b(a);
вызывает конструктор копирования преобразованияB::B(A&)
B b1 = b;
вызывает конструктор копирования по умолчаниюB::B(const B&)
Рассмотрим дополнительные сценарии.
Если у вас естьB b1 = a;
, он вызоветB::B(A&)
и, следовательно, снова не ошибется.Но компилятор отметит ошибку, если
B::B(A&)
было объявленоexplicit
и не будет допущен ни для какогоimplicit conversions
, действуя какconversion function
.Проверьте то же самое здесь.
Конструкторы в языке C++ выполняютинициализацию , в то время как операторы присваивания выполняютприсваивание . Инициализация и назначение - это совершенно разные понятия.
Ссылки в языке C++ могут быть инициализированы, поэтому компилятор не имеет проблем с созданием неявных конструкторов копирования для классов со ссылками. ОператорB b1 = b;
использует неявно созданный конструктор копирования. Я не понимаю, почему вы ожидаете, что он произведет ошибка. Однако сами ссылки не могут быть назначены (переназначены), поэтому компилятор отказывается генерировать неявные операторы присваивания копий для классов со ссылками. Компилятор сообщил вам об этом, выдав предупреждение. Если вы действительно попытаетесь использовать оператор присваивания для классаB
в своей программе, вы получите ошибку.В этом отношении ситуация со ссылками в значительной степени аналогична ситуации с членами
const
: если некоторый класс имеетconst
члены, компилятор не будет иметь никаких проблем с созданием неявного конструктора копирования для этого класса,но откажется генерировать неявное назначение.
Ссылки могут быть инициализированы только один раз и не могут быть изменены. Конструктор допустим, потому что он инициализирует m_a, но копия переназначит m_a, что запрещено.
Вашему образцу не требуется оператор присваивания. VC++ просто предупреждает вас, что он не сможет создать его, если потребуется. Это может быть полезной информацией, если вы пишете библиотеку и забыли предвидеть, что пользователю вашей библиотеки может потребоваться скопировать B.
Если предупреждение вам не нужно, вы можете подавить его (например, с помощью # pragma), или вы можете объявить частный оператор присваивания и не реализовывать его. Если кто-то затем попытается вызвать назначение оператор, он выйдет из строя с ошибкой времени связи.
B b(a);
является допустимым утверждением. Потому чтоB::B(A&)
вызывается при передаче объекта типаA
конструкторуB
.Кстати, с g++ не генерируется предупреждение, как вы упомянули. (И ИМХО, там не должно быть никакого предупреждения, потому что
B b1= b;
вызвал конструктор копирования по умолчаниюB::B(const B&)
.)