Ответ 1
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
всегда является point
, или это никогда не является point
. Вы пытаетесь сделать его point
иногда и не другие. Это логично некогерентно. Если вы создаете логически некогерентные типы, вы можете написать логически некогерентный код.
идея о том, что вы можете безмолвно преобразовать из круга в прямоугольник, это для меня проблема.
Это поднимает важный момент. Преобразование, строго говоря, выглядит так:
circle cr = ...
rectangle rect = cr;
Это плохо сформировано. Когда вы делаете rectangle rect = {cr};
, вы не делаете конверсии. Вы явно вызываете инициализацию списка, которая для агрегата обычно вызывает инициализацию агрегата.
Теперь инициализация списка, безусловно, может выполнять преобразование. Но при условии просто D d = {e};
, не следует ожидать, что это означает, что вы выполняете преобразование из e
в D
Вы инициализируете список объектов типа D
с помощью e
. Это может выполнять преобразование, если E
конвертируется в D
, но эта инициализация все еще может быть действительной, если также могут работать формы инициализации списка без преобразования.
Поэтому неверно говорить, что эта функция делает circle
конвертируемой в rectangle
.