C++ C++;使用已删除的默认构造函数进行直接列表初始化

C++ C++;使用已删除的默认构造函数进行直接列表初始化,c++,c++17,C++,C++17,我有下面的类定义。我加入了一个私有的x,以确保它不是一个聚合 class A { private: int x; public: A() = delete; A(std::initializer_list<int>) { printf("init\n"); } }; A类{ 私人: int x; 公众: A()=删除; (std::initializer_list){printf(“init\n”);} }; 现在如果我用aa=A{}初始化这个对象,它会说A::

我有下面的类定义。我加入了一个私有的
x
,以确保它不是一个聚合

class A {
 private:
  int x;

 public:
  A() = delete;
  A(std::initializer_list<int>) { printf("init\n"); }
};
A类{
私人:
int x;
公众:
A()=删除;
(std::initializer_list){printf(“init\n”);}
};
现在如果我用
aa=A{}
初始化这个对象,它会说
A::A()
被删除了。我猜它正试图调用
A::A()
,但它被删除了。如果我注释掉那一行,那么会自动生成
A::A()
。然后,如果我运行这段代码,我可以看到它正在调用
A::A(std::initializer\u list)

更令人困惑的是,如果我定义
A()=default
,初始化将再次调用
A::A()

有人能告诉我理解这种行为的正确方向吗?谢谢


我正在使用带有标志的G++6.3.0
-std=c++17

行为是正确的

首先:

// Calling
A a = A{}; // equals A a = A();, 
这是列表初始化习惯用法的方便统一。 参考:

案例A: 将导致始终调用初始值设定项列表构造函数,因为外部的花括号表示初始值列表,而内部为零构造元素。->非空初始值设定项列表。。。(再次参见上面的stackoverflow链接!)


顺便说一句,考虑到这是一个非常棘手的部分,我不明白为什么这个问题被否决了……

你列出了很多案例,所以让我们逐一讨论

A() = delete;
A(std::initializer_list<int>) { ... }
A()=删除;
(std::初始值设定项列表){…}
写入
aa=A{}
确实会尝试调用已删除的默认构造函数,而您的代码无法编译

您所拥有的是,并且因为,将执行。这将选择已删除的默认构造函数,这将导致代码格式错误


如果我注释掉那一行,那么会自动生成
A::A()

不,事实并非如此。由于存在采用
初始值设定项\u列表的用户提供的构造函数,因此没有隐式声明的默认构造函数。现在,
aa=A{}
将调用
初始值设定项列表
构造函数,在这种情况下,
初始值设定项列表
将为空


如果我定义
A()=default
,初始化将再次调用
A::A()

这与第一种情况的行为相同,只是默认构造函数显式地
default
ed而不是
delete
d,因此您的代码可以编译


最后,

我加入了一个私有的
x
,以确保它不是一个聚合


没有必要这样做,定义构造函数会使
a
成为非-。

列表初始化遵循非常具体的顺序,如中所述。突出显示了两个相关要点及其相对顺序:

T
类型的对象或引用的列表初始化定义如下:
-如果
T
是聚合类并且[…]
-否则,如果
T
是字符数组并且[…]
-否则,如果
T
是一个聚合,[…]
-否则,如果初始值设定项列表没有元素,并且
T
是具有默认构造函数的类类型,则对象值已初始化。

-否则,如果
T
std::initializer\u list
的专门化,[…]
-否则,如果
T
是类类型,则会考虑构造函数。将枚举适用的构造函数,并通过重载解析(13.3、13.3.1.7)选择最佳构造函数。

-否则,如果初始值设定项列表有一个类型为
E
和[…]
-否则,如果
T
是引用类型,[…]
-否则,如果
T
是具有固定基础类型(7.2)的枚举,[…]
-否则,如果初始值设定项列表没有元素,则对象值已初始化。
-否则,程序的格式就不正确


具有默认构造函数的类类型的空初始值设定项列表是正常构造函数解析之前的一种特殊情况。您的类不是聚合(因为它有一个用户提供的构造函数),所以
a{}
将尝试初始化
a
的值,如果
a
有一个默认构造函数。默认构造函数是否可访问并不重要,重要的是它是否存在。如果它存在并且不可访问(您的第一个案例),那么它的格式就不正确。如果它存在并且可以访问(第三种情况),则会调用它。只有当类没有默认构造函数时,才会枚举构造函数,在这种情况下,
a(初始值设定项列表)
构造函数将使用空的初始值设定项列表(第二种情况)调用

它每次都选择了正确的构造函数。那么在这种情况下,“正确”的构造函数是什么@Paddyth我遇到的问题是,根据
A::A()
是隐式的、默认的还是删除的,它表现出不同的分辨率@在我带着问题来到这里之前,我已经阅读了cppreference上的值初始化。但多亏了你的解释,我有了新的理解。在cppreference上,它表示“如果T是一个没有默认构造函数的类类型…”。所以我猜,隐式默认构造函数满足T没有默认构造函数的情况。这就是它的意思吗?我猜ppl投反对票只是因为这个问题太棘手了,他们一眼就看不出来(@Hot.PxL“如果T是一个没有默认构造函数的类类型,但有一个构造函数采用std::initializer_list,则执行列表初始化。”->list initialization。但是:“如果T是一个没有默认构造函数的类类型,或者有用户提供或删除的默认构造函数,则对象默认初始化,请看:哦!如果我定义了任何构造函数,那么“没有隐式声明的默认构造函数”,这是对的
class A {
     private:
         int x;

     public:
         A(std::initializer_list<int>) { printf("init\n"); }
};
class A {
     private:
         int x;

     public:
         **A() = default;**
         A(std::initializer_list<int>) { printf("init\n"); }
};
A a = {{}};
A() = delete;
A(std::initializer_list<int>) { ... }