在"C++编程语言"第4版第164页:
当我们明确提到要初始化的对象的类型时,我们有两种类型需要考虑:对象的类型和初始化器。例如:
char v1 = 12345; // 12345 is an int int v2 = 'c'; // 'c' is a char T v3 = f();
通过对此类定义使用{}-初始值设定项语法,我们将不幸转换的机会:
char v1 {12345}; // error : narrowing int v2 {'c'}; // fine: implicit char->int conversion T v3 {f()}; // works if and only if the type of f() can be implicitly converted to a T
我不太理解句子minimize the chances for unfortunate conversions
和对T v3 {f()};
的注释works if and only if the type of f() can be implicitly converted to a T
。考虑以下两种情况:
- a)如果T有一个采用f()类型参数的显式构造函数
- b) 如果f()的类型有一个到某种类型X的转换运算符,而T有一个接受X类型参数的构造函数
对于这两种情况,f()的类型都不能隐式转换为t,但T v3 {f()}
的格式很好,所以至少该注释的only if
部分似乎不合适?(也不确定if
部分是否正确。)
对于这两种情况,都是T v3 = f();
格式错误,那么minimize the chances for unfortunate conversions
在这里是什么意思?似乎{}-初始值设定项实际上接受了更多的转换形式(是否不幸是另一个问题)。(在v1
的情况下说明了防止变窄,这很清楚。我对v3
感到困惑。)
它实际上以默认方式之外的其他方式进行转换。
使用您的一个示例,char x = 12345;
实际上会将这个值减少到最后8位,因为这是char的大小。用{}
表示法,由于它不接近,所以会产生误差。
有时(ab)并不是用来获得特殊效果的,所以在新的C++中,它被保留为默认选项,而新的符号用于提供更好的行为,同时减少错误空间。
转换:
char v1 = 12345;
是不幸,因为它几乎肯定不是程序员想要的:
- 要么我们想要一个可以表示值12345的类型
- 或者我们使用了正确的类型,但使用了错误的值
对于v3
,同样适用,但适用于更复杂的上下文。第一个代码段将强制编译器考虑用户定义的转换序列。这增加了出错的可能性;毕竟,我们可能会错误地实现转换运算符,造成拼写错误,或者设法在方孔中安装一个圆形销钉。使用复制列表初始化,我们排除了用户定义的转换序列,使转换更加安全。
有关为什么会发生这种情况的示例和详细解释,请参阅此问题。
v3
初始化注释:
T v3 {f()}; // works if and only if the type of f() can be implicitly converted to a T
不是严格正确的。当且仅当f()
的类型可以显式转换为T
时,该初始化才有效。此语法:
T v3 = {f()}; // truly does work if and only if the type of f()
// can be implicitly converted to a T
(复制列表初始化而不是直接列表初始化)确实需要隐式转换。这个程序说明了区别:
struct T {
explicit T(int) {}
};
int f() { return 0; }
int main() {
T v3 = {f()};
}
其中初始化将被诊断为格式错误,因为它为T
(Coliru的实时演示)选择了一个显式构造函数。