问题描述
struct Point{
int x;
int y;
int z;
};
Point p{.x=47,.y=1701,.z=0};
但是如果我添加一个构造函数,那么我将被禁止使用漂亮的指定的initalizers语法:
struct Point{
Point(int x,int y,int z = 0): x(x),y(y),z(z){}
int x;
int y;
int z;
};
static Point p{.x=47,.z = 0};
我是否遗漏了一些显而易见的东西(如果指定的初始化器与具有公共成员但不是集合的结构/类一起使用,为什么会很糟糕?或者这只是缺少的功能而未添加到标准中?
>解决方法
聚合初始化(包括使用设计好的初始化程序进行的初始化)规避了类的构造函数。
这对于聚合来说不是问题,因为不允许它们具有用户定义的构造函数。但是,如果允许使用用户提供的构造函数(做一些有用的事情)对类进行此类初始化,则可能是有害的。
考虑以下示例:
class A
{
static std::map<A *,int> &Indices()
{
static std::map<A *,int> ret;
return ret;
}
public:
int dummy = 0;
A(int index)
{
Indices().emplace(this,index);
}
A(const A &) = delete;
A &operator=(const A &) = delete;
~A()
{
auto it = Indices().find(this);
std::cout << "Deleting #" << it->second << '\n';
Indices().erase(it);
}
};
如果您能够进行A{.dummy = 42};
,则可以在析构函数中获得UB,并且无法防止这种使用。
指定的初始化器,其中的功能从C提升而来。大多数C ++编译器也是C编译器,并且首先是C功能。
他们添加了一个限制(初始化程序必须按顺序进行),并将其应用于与C类型匹配的C ++类型,并将其纳入C ++。大多数主要的C ++编译器已经将其作为C ++扩展(没有限制);通过编译器实现者检查了该限制是否合理,然后添加该功能的“成本”确实很低。
一旦有了构造函数,它将成为一个更大的语言问题。初始化程序是否引用构造函数参数?如果是,我们将遇到参数名称不唯一的问题。如果不是,那么当构造函数设置一个值而初始化程序设置一个不同的值时,我们该如何处理呢?
基本上,我们需要按名称分配函数参数,以使用构造函数获取明智的指定初始值设定项。这是一项新功能,不是简单地从C中取消的功能。
(针对命名参数的)解决方法是:
struct RawPoint{
int x = 0;
int y = 0;
int z = 0;
};
struct Point {
Point( int x_,int y_,int z_ = 0 ):
x(x_),y(y_),z(z_)
{}
explicit Point( RawPoint pt ):
Point( pt.x,pt.y,pt.z )
{}
int x,y,z;
};
然后您可以做:
Point pt( {.x=3} );
通过访问RawPoint
的指定初始化器功能。
这与在函数调用中指定初始化程序的方式相同。
这也有效:
struct Point:RawPoint {
Point( int x,int y,int z = 0 ):
RawPoint{x,z}
{}
explicit Point( RawPoint pt ):
RawPoint( pt )
{}
};