为什么用这两行代码打印出不同的结果?
std::cout << std::string{6, 's'}
std::cout << std::string(6, 's')
答案 0 :(得分:8)
由于std::string
的constructor占用std::initializer_list
,第一个示例将使用该构造函数创建一个包含两个字符的字符串对象。像这样的初始化称为list initialization。
第二个示例将创建一个包含六个字符的字符串对象,全部初始化为's'
。这种初始化形式称为direct initialization。
列表初始化和直接初始化可以是相同的,除了大型类型到较小类型的可能转换被禁止用于列表初始化,并且如果该类具有构造函数采用std::initializer_list
,则注意到这一点。
答案 1 :(得分:3)
这是因为在第一种情况下,编译器更喜欢另一个重载并使用符号char(6)和&#39;来初始化字符串。您可以通过将6更改为smth来检查它,如35可打印字符。 虽然有用并且解决了最令人烦恼的解析问题,但{}构造有它的警告。
答案 2 :(得分:3)
原因是调用了不同的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个字符组成的字符串。
答案 3 :(得分:1)
使用{}
进行初始化称为列表初始化。列表初始化在几个方面遵循与正常初始化不同的规则,但这里的重要案例与std::initializer_list
有关。
规则是:如果您是列表初始化类类型T
,并且该类类型具有带std::initializer_list<U>
的构造函数,并且初始化列表中的每个元素都可以转换为U
,然后选择该构造函数。这种情况在任何其他构造函数被考虑之前发生,即使构造函数最终都是格式错误(例如由于转换变窄)。记住这条规则很重要 - std::initializer_list
在这种情况下总是优先考虑!
这里std::string
的两个相关构造函数是(假设std::string
是一个类型而不是simpilcity的类模板特化):
string(std::initializer_list<char> init); // #1
string(size_t count, char ch ); // #2
当你编写std::string{6, 's'}
时,这是列表初始化,所以我们看看是否有一个有效的std::initializer_list
构造函数 - 并且有! int
和char
都可转换为char
,因此已被选中。在这种情况下,没有缩小转化,因为6
适合char
,因此选择并使用。甚至从未考虑过第二个构造函数。请注意,std::string{300, '.'}
格式不正确,因为我们选择了std::initializer_list<char>
构造函数,但从300
到char
的转换正在缩小。即使其他构造函数可以工作,也没关系,我们选择std::initializer_list<char>
并且错误。
但是当你写std::string(6, 's')
时,那不是列表初始化。所有构造函数都在这里考虑。 std::initializer_list
构造函数不匹配 - 您无法从std::initializer_list<char>
初始化int
- 但第二个构造函数是,所以它已被选中。这是更正常,熟悉的重载分辨率。
一个好的经验法则是 - {}
- 初始化用于初始化聚合(无论如何都没有构造函数)或初始化特定元素集中的容器或消除最令人烦恼的解析。如果您没有做任何这些事情,请使用()
s。