struct D и struct E представляют два совершенно не связанных типа.
Но это не совсем не связанные типы. Они оба имеют одинаковый тип базового класса. Это означает, что каждый D
неявно конвертируется в B
. И поэтому каждый D
является B
. Таким образом, E e{d};
ничем не отличается от E e{b};
в отношении вызванной операции.
Вы не можете отключить неявное преобразование в базовые классы.
Если это действительно беспокоит вас, единственное решение - предотвратить агрегатную инициализацию, предоставив соответствующий конструктор (-ы), который перенаправляет значения членам.
Что касается того, делает ли это агрегатную инициализацию более опасной, я так не думаю. Вы можете воспроизвести вышеуказанные обстоятельства с помощью следующих структур:
struct B { int i; };
struct D { B b; char j; operator B() {return b;} };
struct E { B b; float k; };
Так что нечто подобное всегда было возможно. Я не думаю, что использование неявного преобразования базового класса делает его намного «хуже».
Более глубокий вопрос - почему пользователь попытался инициализировать E
с D
для начала.
идея о том, что вы можете молча преобразовать из круга в прямоугольник, для меня это проблема.
У вас возникла бы такая же проблема, если бы вы сделали это:
struct rectangle
{
rectangle(point p);
int sx; int sy;
point p;
};
Вы можете не только выполнить rectangle r{c};
, но и rectangle r(c)
.
Ваша проблема в том, что вы неправильно используете наследование. Вы говорите об отношениях между circle
, rectangle
и point
, которые вы не имеете в виду. И поэтому компилятор позволяет вам делать то, что вы не хотели делать.
Если бы вы использовали сдерживание вместо наследования, это не было бы проблемой:
struct point { int x; int y; };
struct circle { point center; int r; };
struct rectangle { point top_left; int sx; int sy; };
void move( point& p );
void f( circle c )
{
move( c ); // Error, as it should, since a circle is not a point.
rectangle r1( c ); // Error, as it should be
rectangle r2{ c }; // Error, as it should be.
}
Либо circle
равен всегда a point
, либо никогда a point
. Вы пытаетесь сделать это point
иногда, а не другими. Это логически бессвязно. Если вы создаете логически несвязные типы, то вы можете написать логически несвязный код.
идея, что вы можете молча преобразовать из круга в прямоугольник, для меня это проблема.
Это поднимает важный вопрос. Конвертация, строго говоря, выглядит так:
circle cr = ...
rectangle rect = cr;
Это плохо сформировано. Когда вы делаете rectangle rect = {cr};
, вы не делаете преобразование. Вы явно вызываете инициализацию списка, которая для агрегата обычно вызывает инициализацию агрегата.
Теперь инициализация списка, безусловно, может выполнить преобразование. Но учитывая просто D d = {e};
, не следует ожидать, что это означает, что вы выполняете преобразование из e
в D
. Вы инициализируете список объекта типа D
с e
. Это может выполнить преобразование, если E
может быть преобразовано в D
, но эта инициализация все еще может быть действительной, если формы инициализации списка без преобразования тоже могут работать.
Поэтому неправильно говорить, что эта функция делает circle
конвертируемым в rectangle
.