C++ 复制列表初始化和传统的复制初始化之间有什么区别吗?
除了支持多个参数、不允许缩小转换、使用std::initializer\u list参数匹配构造函数外,复制列表初始化与传统复制初始化还有什么不同 具体来说,假设有两种用户定义的类型,C++ 复制列表初始化和传统的复制初始化之间有什么区别吗?,c++,c++11,C++,C++11,除了支持多个参数、不允许缩小转换、使用std::initializer\u list参数匹配构造函数外,复制列表初始化与传统复制初始化还有什么不同 具体来说,假设有两种用户定义的类型,A和B: class A {...}; class B {...}; B b; A a1 = {b}; A a2 = b; A和B的哪种定义会对这两种初始化形式产生影响?e、 g.a和B是否有某种定义,使得其中一个初始化合法,但另一个非法,或者两者都合法,但语义不同,或者两者都非法,原因不同 (假设A没有采用s
A
和B
:
class A {...};
class B {...};
B b;
A a1 = {b};
A a2 = b;
A
和B
的哪种定义会对这两种初始化形式产生影响?e、 g.a
和B
是否有某种定义,使得其中一个初始化合法,但另一个非法,或者两者都合法,但语义不同,或者两者都非法,原因不同
(假设A
没有采用std::initializer\u list参数的构造函数。)
编辑:添加指向我的一个相关问题的链接:复制初始化始终考虑复制构造函数的可用性,而复制列表初始化则不考虑
class B {};
struct A
{
A(B const&) {}
A(A const&) = delete;
};
B b;
A a1 = {b}; // this compiles
A a2 = b; // this doesn't because of deleted copy-ctor
这是因为复制列表初始化与直接列表初始化相同,只是在一种情况下-如果A(B const&)
beenexplicit
,前者将失败,而后者将工作
class B {};
struct A
{
explicit A(B const&) {}
};
int main()
{
B b;
A a1{b}; // compiles
A a2 = {b}; // doesn't compile because ctor is explicit
}
可能新副本列表初始化的行为被定义为“良好”和一致,但由于向后兼容,旧副本初始化的“奇怪”行为无法更改。
如您所见,本条款中的列表初始化规则对于直接表单和复制表单是相同的。
与
显式相关的差异仅在过载解决方案一章中描述。但对于传统的初始化,直接和复制表单并不相同。
传统初始化和大括号初始化是分开定义的,因此总有一些(可能是无意中)细微的差异
我可以从标准摘录中看到的差异:
1.已经提到的差异
- 不允许缩小转换范围
- 可以有多个参数
- 大括号语法首选初始值设定项列表构造函数(如果存在):
struct A
{
A(int i_) : i (i_) {}
A(std::initializer_list<int> il) : i (*il.begin() + 1) {}
int i;
}
A a1 = 5; // a1.i == 5
A a2 = {5}; // a2.i = 6
3.存在转换运算符时参考初始化的不同行为
大括号初始化无法使用转换为引用类型的运算符
struct S
{
operator int&() { return some_global_int;}
};
int& iref1 = s; // OK
int& iref2 = {s}; // ill-formed
4.类类型的对象与其他类型的对象在初始化时的一些细微差别
这些差异在本答案末尾的标准摘录中以[*]标记
- 旧的初始化使用用户定义的转换序列的概念(特别是,如上所述,需要拷贝构造函数的可用性)
- 大括号初始化只是在适用的构造函数之间执行重载解析,即大括号初始化不能使用转换为类类型的运算符
这些差异导致了一些不太明显的情况,比如
struct Intermediate {};
struct S
{
operator Intermediate() { return {}; }
operator int() { return 10; }
};
struct S1
{
S1(Intermediate) {}
};
S s;
Intermediate im1 = s; // OK
Intermediate im2 = {s}; // ill-formed
S1 s11 = s; // ill-formed
S1 s12 = {s}; // OK
// note: but brace initialization can use operator of conversion to int
int i1 = s; // OK
int i2 = {s}; // OK
5.过载分辨率的差异
- 显式构造函数的不同处理
见13.3.1.7列表初始化初始化
在复制列表初始化中,如果选择了显式构造函数,则
初始化格式不正确。[注:这与其他方法不同
仅转换施工人员的情况(13.3.1.3、13.3.1.4)
复制初始化时考虑。此限制仅适用
如果此初始化是过载最终结果的一部分
决议-结束说明]
如果你能看到更多的差异或以某种方式纠正我的答案(包括语法错误),请这样做
以下是相关的(但很长)摘录(我还没有找到一种方法将它们隐藏在扰流板下):
所有这些都位于第8.5章初始值设定项中
8.5初始化者
- 如果初始值设定项是一个(非括号)带括号的init列表,则
对象或引用列表已初始化(8.5.4)
- 如果目的地类型为参考类型,请参见8.5.3
- 如果目标类型是字符数组,则为
char16\t
的数组
char32\u t
数组,或wchar\u t
数组,且初始值设定项为
字符串文字,见8.5.2
- 如果初始值设定项为
()
,则对象为
值已初始化
- 否则,如果目标类型是数组,
这个程序格式不好
- 如果目标类型是(可能是
cv(合格)等级类型:
- 如果初始化是
直接初始化,或者如果是复制初始化
源类型的cv非限定版本与类相同,或
派生类,目标类,构造函数是
考虑过的。列举了适用的施工人员(13.3.1.3),以及
通过过载分辨率(13.3)选择最佳。这个
调用如此选择的构造函数来初始化对象,并使用
初始值设定项表达式或表达式列表作为其参数。如果没有
构造函数应用,或者重载解析不明确
初始化格式不正确
- [*]否则(即
其余的复制初始化情况),用户定义的转换
可以从源类型转换到目标类型的序列
输入或(当使用转换函数时)到派生类
其中按13.3.1.4所述列举,最佳为
通过过载分辨率(13.3)选择。如果无法进行转换
完成或不明确,初始化格式不正确。功能
以初始值设定项表达式作为参数调用selected;如果
函数是一个构造函数,调用初始化
cv目标类型的不合格版本。临时的是
prvalue。结果
struct Intermediate {};
struct S
{
operator Intermediate() { return {}; }
operator int() { return 10; }
};
struct S1
{
S1(Intermediate) {}
};
S s;
Intermediate im1 = s; // OK
Intermediate im2 = {s}; // ill-formed
S1 s11 = s; // ill-formed
S1 s12 = {s}; // OK
// note: but brace initialization can use operator of conversion to int
int i1 = s; // OK
int i2 = {s}; // OK