为什么我不能将指定的初始化器与非聚合结构一起使用?

问题描述

C ++具有一个不错的新功能

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 )
 {}
};