Has the C++17 extension to aggregate initialization made brace initialization dangerous?

  • A+
Category:Languages

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.


struct D and struct E represent two completely unrelated types.

But they're not "completely unrelated" types. They both have the same base class type. This means that every D is implicitly convertible to a B. And therefore every D is a B. So doing E e{d}; is no different from E e{b}; in terms of the invoked operation.

You cannot turn off implicit conversion to base classes.

If this truly bothers you, the only solution is to prevent aggregate initialization by providing an appropriate constructor(s) that forwards the values to the members.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: