使用删除的默认构造函数的 C++ 直接列表初始化
C++ direct list initialization with deleted default constructor
我有以下 class 定义。我包含了一个私有 x
以确保它不是聚合。
class A {
private:
int x;
public:
A() = delete;
A(std::initializer_list<int>) { printf("init\n"); }
};
现在如果我用 A a = A{}
初始化这个对象,它会说 A::A()
被删除。我猜它正在尝试调用 A::A()
但它已被删除。如果我注释掉那一行,那么 A::A()
就会自动生成。然后如果我 运行 这段代码,我可以看到它正在调用 A::A(std::initializer_list<int>)
!
更令人困惑的是,如果我定义 A() = default
,初始化会再次调用 A::A()
。
谁能指出我理解这种行为的正确方向?谢谢!
我正在使用带有标志 -std=c++17
的 G++ 6.3.0。
行为正确。
首先:
// Calling
A a = A{}; // equals A a = A();,
这是列表初始化习惯用法的方便统一。
参考:
案例一:
class A {
private:
int x;
public:
**A() = delete;**
A(std::initializer_list<int>) { printf("init\n"); }
};
上面说了,A a = A{} 会是... = A(),删掉
案例 B:
class A {
private:
int x;
public:
A(std::initializer_list<int>) { printf("init\n"); }
};
在这种情况下,没有提供默认构造函数,因为您定义了初始化列表构造函数。因此 {} 被隐式地转换为一个空的初始化列表!
案例 C:
class A {
private:
int x;
public:
**A() = default;**
A(std::initializer_list<int>) { printf("init\n"); }
};
在这种情况下,空的默认构造函数可用,A{} 被转换回 A(),导致它被调用。
请参阅以下页面并仔细阅读。
http://en.cppreference.com/w/cpp/utility/initializer_list
http://en.cppreference.com/w/cpp/language/value_initialization
最后:
使用:
A a = {{}};
会导致始终调用初始化列表构造函数,因为外花括号表示初始化列表,而内部是零构造元素。 -> 非空初始化列表...(再次参见上面的 Whosebug link!)
顺便说一句,考虑到这是一个非常棘手的部分,我不明白为什么这个问题被否决了...
你列出的案例很多,让我们一一分析。
A() = delete;
A(std::initializer_list<int>) { ... }
写A a = A{};
确实会尝试调用已删除的默认构造函数,并且您的代码无法编译。
你上面的是 list initialization, and because the braced-init-list is empty, value initialization 将被执行。这会选择已删除的默认构造函数,这会使您的代码格式错误。
If I comment out that line, so A::A()
is automatically generated
不,事实并非如此。由于存在采用 initializer_list
的用户提供的构造函数,因此没有隐式声明的默认构造函数。现在,A a = A{};
将调用 initializer_list
构造函数,在这种情况下 initializer_list
将为空。
if I define A() = default
, the initialization calls A::A()
again
这与第一种情况的行为相同,除了默认构造函数是显式 default
ed 而不是 delete
d,因此您的代码可以编译。
最后,
I included a private x
to make sure it is not an aggregate
没有这个必要,定义构造函数使 A
成为非 aggregate.
列表初始化遵循非常具体的顺序,如 [dcl.init.list]/3 中所述。突出显示了两个相关的要点及其相对顺序:
List-initialization of an object or reference of type T
is defined as follows:
— If T
is an aggregate class and [...]
— Otherwise, if T
is a character array and [...]
— Otherwise, if T
is an aggregate, [...]
— Otherwise, if the initializer list has no elements and T
is a class type with a default constructor, the object is value-initialized.
— Otherwise, if T
is a specialization of std::initializer_list<E>
, [...]
— Otherwise, if T
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7).
— Otherwise, if the initializer list has a single element of type E
and [...]
— Otherwise, if T
is a reference type, [...]
— Otherwise, if T
is an enumeration with a fixed underlying type (7.2), [...]
— Otherwise, if the initializer list has no elements, the object is value-initialized.
— Otherwise, the program is ill-formed.
具有默认构造函数的 class 类型的空初始值设定项列表是一种先于正常构造函数解析的特殊情况。您的 class 不是一个聚合(因为它有一个用户提供的构造函数),所以如果 A
有一个默认构造函数,A{}
将尝试值初始化 A
。默认构造函数是否可访问并不重要,只有它存在才重要。如果它存在并且不可访问(你的第一个案例),那就是错误的。如果它存在并且可以访问(你的第三种情况),那么它就会被调用。仅当您的 class 没有默认构造函数时才会枚举构造函数,在这种情况下,将使用空的初始化列表调用 A(initializer_list<int> )
构造函数(您的第二种情况)。
我有以下 class 定义。我包含了一个私有 x
以确保它不是聚合。
class A {
private:
int x;
public:
A() = delete;
A(std::initializer_list<int>) { printf("init\n"); }
};
现在如果我用 A a = A{}
初始化这个对象,它会说 A::A()
被删除。我猜它正在尝试调用 A::A()
但它已被删除。如果我注释掉那一行,那么 A::A()
就会自动生成。然后如果我 运行 这段代码,我可以看到它正在调用 A::A(std::initializer_list<int>)
!
更令人困惑的是,如果我定义 A() = default
,初始化会再次调用 A::A()
。
谁能指出我理解这种行为的正确方向?谢谢!
我正在使用带有标志 -std=c++17
的 G++ 6.3.0。
行为正确。
首先:
// Calling
A a = A{}; // equals A a = A();,
这是列表初始化习惯用法的方便统一。 参考:
案例一:
class A {
private:
int x;
public:
**A() = delete;**
A(std::initializer_list<int>) { printf("init\n"); }
};
上面说了,A a = A{} 会是... = A(),删掉
案例 B:
class A {
private:
int x;
public:
A(std::initializer_list<int>) { printf("init\n"); }
};
在这种情况下,没有提供默认构造函数,因为您定义了初始化列表构造函数。因此 {} 被隐式地转换为一个空的初始化列表!
案例 C:
class A {
private:
int x;
public:
**A() = default;**
A(std::initializer_list<int>) { printf("init\n"); }
};
在这种情况下,空的默认构造函数可用,A{} 被转换回 A(),导致它被调用。
请参阅以下页面并仔细阅读。 http://en.cppreference.com/w/cpp/utility/initializer_list http://en.cppreference.com/w/cpp/language/value_initialization
最后:
使用:
A a = {{}};
会导致始终调用初始化列表构造函数,因为外花括号表示初始化列表,而内部是零构造元素。 -> 非空初始化列表...(再次参见上面的 Whosebug link!)
顺便说一句,考虑到这是一个非常棘手的部分,我不明白为什么这个问题被否决了...
你列出的案例很多,让我们一一分析。
A() = delete;
A(std::initializer_list<int>) { ... }
写A a = A{};
确实会尝试调用已删除的默认构造函数,并且您的代码无法编译。
你上面的是 list initialization, and because the braced-init-list is empty, value initialization 将被执行。这会选择已删除的默认构造函数,这会使您的代码格式错误。
If I comment out that line, so
A::A()
is automatically generated
不,事实并非如此。由于存在采用 initializer_list
的用户提供的构造函数,因此没有隐式声明的默认构造函数。现在,A a = A{};
将调用 initializer_list
构造函数,在这种情况下 initializer_list
将为空。
if I define
A() = default
, the initialization callsA::A()
again
这与第一种情况的行为相同,除了默认构造函数是显式 default
ed 而不是 delete
d,因此您的代码可以编译。
最后,
I included a private
x
to make sure it is not an aggregate
没有这个必要,定义构造函数使 A
成为非 aggregate.
列表初始化遵循非常具体的顺序,如 [dcl.init.list]/3 中所述。突出显示了两个相关的要点及其相对顺序:
List-initialization of an object or reference of type
T
is defined as follows:
— IfT
is an aggregate class and [...]
— Otherwise, ifT
is a character array and [...]
— Otherwise, ifT
is an aggregate, [...]
— Otherwise, if the initializer list has no elements andT
is a class type with a default constructor, the object is value-initialized.
— Otherwise, ifT
is a specialization ofstd::initializer_list<E>
, [...]
— Otherwise, ifT
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7).
— Otherwise, if the initializer list has a single element of typeE
and [...]
— Otherwise, ifT
is a reference type, [...]
— Otherwise, ifT
is an enumeration with a fixed underlying type (7.2), [...]
— Otherwise, if the initializer list has no elements, the object is value-initialized.
— Otherwise, the program is ill-formed.
具有默认构造函数的 class 类型的空初始值设定项列表是一种先于正常构造函数解析的特殊情况。您的 class 不是一个聚合(因为它有一个用户提供的构造函数),所以如果 A
有一个默认构造函数,A{}
将尝试值初始化 A
。默认构造函数是否可访问并不重要,只有它存在才重要。如果它存在并且不可访问(你的第一个案例),那就是错误的。如果它存在并且可以访问(你的第三种情况),那么它就会被调用。仅当您的 class 没有默认构造函数时才会枚举构造函数,在这种情况下,将使用空的初始化列表调用 A(initializer_list<int> )
构造函数(您的第二种情况)。