There seems to be a general consensus that brace initialization should be preferred over other forms of initialization, however since the introduction of the C++17 extension to aggregate initialization there seems to be a risk of unintended conversions. Consider the following code:
struct B { int i; };
struct D : B { char j; };
struct E : B { float k; };
void f( const D& d )
{
E e1 = d; // error C2440: 'initializing': cannot convert from 'D' to 'E'
E e2( d ); // error C2440: 'initializing': cannot convert from 'D' to 'E'
E e3{ d }; // OK in C++17 ???
}
struct F
{
F( D d ) : e{ d } {} // OK in C++17 ???
E e;
};
In the code above struct D
and struct E
represent two completely unrelated types. So it is a surprise to me that as of C++17 you can "convert" from one type to another type without any warning if you use brace (aggregate) initialization.
What would you recommend to avoid these types of accidental conversions? Or am I missing something?
PS: The code above was tested in Clang, GCC and the latest VC++ - they are all the same.
Update: In response to the answer from Nicol. Consider a more practical example:
struct point { int x; int y; };
struct circle : point { int r; };
struct rectangle : point { int sx; int sy; };
void move( point& p );
void f( circle c )
{
move( c ); // OK, makes sense
rectangle r1( c ); // Error, as it should be
rectangle r2{ c }; // OK ???
}
I can understand that you can view a circle
as a point
, because circle
has point
as base class, but the idea that you can silently convert from a circle to a rectangle, that to me is a problem.
Update 2: Because my poor choice of class name seems to be clouding the issue for some.
struct shape { int x; int y; };
struct circle : shape { int r; };
struct rectangle : shape { int sx; int sy; };
void move( shape& p );
void f( circle c )
{
move( c ); // OK, makes sense
rectangle r1( c ); // Error, as it should be
rectangle r2{ c }; // OK ???
}