用“()”调用构造函数不同于“{}”
Calling constructor with "()" is different from "{}"
为什么这两行代码打印出不同的结果?
std::cout << std::string{6, 's'}
std::cout << std::string(6, 's')
因为std::string
have a constructor taking an std::initializer_list
, the first example will use that constructor to create a string object with two characters. Initialization like this is called list initialization.
第二个例子将创建一个包含六个字符的字符串对象,全部初始化为's'
。这种初始化形式称为 direct initialization.
列表初始化和直接初始化可以是相同的,除了列表初始化禁止从较大类型到较小类型的可能转换,并且如果 class 有一个采用 [= 的构造函数,请注意此处11=].
发生这种情况是因为在第一种情况下,编译器更喜欢另一个重载并使用符号 char(6) 和 's' 初始化字符串。您可以通过将 6 更改为类似 35 的可打印字符来检查它。
尽管有用并解决了最棘手的解析问题,{} 构造也有一些注意事项。
原因是调用的constructors不同
std::string{6, 's'}
此代码使用初始化列表调用构造函数:
basic_string( std::initializer_list<CharT> init,
const Allocator& alloc = Allocator() );
因此 6
被转换为 char
并且打印了一个由两个字符组成的字符串。
std::string(6, 's')
此代码调用下一个构造函数:
basic_string( size_type count,
CharT ch,
const Allocator& alloc = Allocator() );
所以打印了一个由 6 个字符组成的字符串。
用{}
s 进行的初始化称为列表初始化。列表初始化在几个方面遵循与正常初始化不同的规则,但这里的重要情况与 std::initializer_list
.
有关
规则是:如果您正在列表初始化 class 类型 T
,并且 class 类型有一个采用 std::initializer_list<U>
的构造函数,并且初始化列表中的每个元素都可以转换为 U
,然后该构造函数被 selected。这发生在甚至考虑任何其他构造函数之前,即使该构造函数最终格式错误(例如由于缩小转换)。记住这条规则很重要 - 在这种情况下 std::initializer_list
总是完全优先!
这里 std::string
的两个相关构造函数是(假设 std::string
是一个类型而不是 class 简单的模板特化):
string(std::initializer_list<char> init); // #1
string(size_t count, char ch ); // #2
当您编写 std::string{6, 's'}
时,即列表初始化,因此我们查看是否存在有效的 std::initializer_list
构造函数 - 确实存在! int
和 char
都可以转换为 char
,所以它是 selected。在这种情况下,没有缩小转换,因为 6
适合 char
,所以它是 selected 和使用。甚至从未考虑过第二个构造函数。请注意 std::string{300, '.'}
格式错误,因为我们 select std::initializer_list<char>
构造函数但是从 300
到 char
的转换正在缩小。即使其他构造函数 可以工作 ,也没关系,我们选择 std::initializer_list<char>
和错误。
但是当你写 std::string(6, 's')
时,那不是列表初始化。此处考虑了所有构造函数。 std::initializer_list
构造函数不匹配 - 你不能从 int
初始化 std::initializer_list<char>
- 但第二个构造函数匹配,所以它是 selected。这是工作中更正常、更熟悉的重载解决方案。
一个好的经验法则是 - {}
- 初始化用于初始化聚合(无论如何都没有构造函数)或从特定元素集初始化容器或消除最令人烦恼的解析的歧义。如果您没有做任何这些事情,请使用 ()
s。
为什么这两行代码打印出不同的结果?
std::cout << std::string{6, 's'}
std::cout << std::string(6, 's')
因为std::string
have a constructor taking an std::initializer_list
, the first example will use that constructor to create a string object with two characters. Initialization like this is called list initialization.
第二个例子将创建一个包含六个字符的字符串对象,全部初始化为's'
。这种初始化形式称为 direct initialization.
列表初始化和直接初始化可以是相同的,除了列表初始化禁止从较大类型到较小类型的可能转换,并且如果 class 有一个采用 [= 的构造函数,请注意此处11=].
发生这种情况是因为在第一种情况下,编译器更喜欢另一个重载并使用符号 char(6) 和 's' 初始化字符串。您可以通过将 6 更改为类似 35 的可打印字符来检查它。 尽管有用并解决了最棘手的解析问题,{} 构造也有一些注意事项。
原因是调用的constructors不同
std::string{6, 's'}
此代码使用初始化列表调用构造函数:
basic_string( std::initializer_list<CharT> init,
const Allocator& alloc = Allocator() );
因此 6
被转换为 char
并且打印了一个由两个字符组成的字符串。
std::string(6, 's')
此代码调用下一个构造函数:
basic_string( size_type count,
CharT ch,
const Allocator& alloc = Allocator() );
所以打印了一个由 6 个字符组成的字符串。
用{}
s 进行的初始化称为列表初始化。列表初始化在几个方面遵循与正常初始化不同的规则,但这里的重要情况与 std::initializer_list
.
规则是:如果您正在列表初始化 class 类型 T
,并且 class 类型有一个采用 std::initializer_list<U>
的构造函数,并且初始化列表中的每个元素都可以转换为 U
,然后该构造函数被 selected。这发生在甚至考虑任何其他构造函数之前,即使该构造函数最终格式错误(例如由于缩小转换)。记住这条规则很重要 - 在这种情况下 std::initializer_list
总是完全优先!
这里 std::string
的两个相关构造函数是(假设 std::string
是一个类型而不是 class 简单的模板特化):
string(std::initializer_list<char> init); // #1
string(size_t count, char ch ); // #2
当您编写 std::string{6, 's'}
时,即列表初始化,因此我们查看是否存在有效的 std::initializer_list
构造函数 - 确实存在! int
和 char
都可以转换为 char
,所以它是 selected。在这种情况下,没有缩小转换,因为 6
适合 char
,所以它是 selected 和使用。甚至从未考虑过第二个构造函数。请注意 std::string{300, '.'}
格式错误,因为我们 select std::initializer_list<char>
构造函数但是从 300
到 char
的转换正在缩小。即使其他构造函数 可以工作 ,也没关系,我们选择 std::initializer_list<char>
和错误。
但是当你写 std::string(6, 's')
时,那不是列表初始化。此处考虑了所有构造函数。 std::initializer_list
构造函数不匹配 - 你不能从 int
初始化 std::initializer_list<char>
- 但第二个构造函数匹配,所以它是 selected。这是工作中更正常、更熟悉的重载解决方案。
一个好的经验法则是 - {}
- 初始化用于初始化聚合(无论如何都没有构造函数)或从特定元素集初始化容器或消除最令人烦恼的解析的歧义。如果您没有做任何这些事情,请使用 ()
s。