似乎有一个普遍的共识,即使用花括号初始化比其他形式的初始化更好,然而自从引入了C++17聚合初始化扩展之后,就存在意外转换的风险。请考虑以下代码:
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;
};
在上面的代码中,struct D
和struct E
代表两种完全不相关的类型。因此,令人惊讶的是,在C++17中,如果使用大括号(聚合)初始化,您可以“转换”为另一种类型而没有任何警告。您会建议采取什么措施来避免这些意外的转换?或者我可能漏掉了什么?
附言:上面的代码已在Clang、GCC和最新的VC++中进行了测试-它们都是一样的。
更新:针对Nicol的答复,请考虑一个更实际的例子:
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 ???
}
我可以理解,您可以将一个圆
视为一个点
,因为圆
具有基类点
,但是从圆形静默地转换为矩形的想法,对我来说是个问题。
更新2: 因为我的类名选择不好似乎让一些人感到困惑了。
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 ???
}
ShapeWithCenter
,那么情况就会在很大程度上(但不是完全)变得明显。现在,我同意花括号初始化和表面上预期的构造函数之间的差异仍然很危险,但这并不是新鲜事。 - Max LanghofManager
和Assistant
与Employee
之间具有“是一个”关系比circle
和rectangle
与point
更有意义。圆具有点,但它们不是点;您不能将其与点互换使用。然而,如果没有成为员工,经理就不能成为经理。 我还敢打赌,Stroustrup的所有类型都不是聚合体,因此这并不重要。 - Nicol Bolas