使用“()”调用构造函数与“{}”不同

时间:2017-06-26 12:48:38

标签: c++ string constructor c++14 std

为什么用这两行代码打印出不同的结果?

std::cout << std::string{6, 's'}
std::cout << std::string(6, 's')

4 个答案:

答案 0 :(得分:8)

由于std::stringconstructor占用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构造函数 - 并且有! intchar都可转换为char,因此已被选中。在这种情况下,没有缩小转化,因为6适合char,因此选择并使用。甚至从未考虑过第二个构造函数。请注意,std::string{300, '.'}格式不正确,因为我们选择了std::initializer_list<char>构造函数,但从300char的转换正在缩小。即使其他构造函数可以工作,也没关系,我们选择std::initializer_list<char>并且错误。

但是当你写std::string(6, 's')时,那不是列表初始化。所有构造函数都在这里考虑。 std::initializer_list构造函数不匹配 - 您无法从std::initializer_list<char>初始化int - 但第二个构造函数是,所以它已被选中。这是更正常,熟悉的重载分辨率。

一个好的经验法则是 - {} - 初始化用于初始化聚合(无论如何都没有构造函数)或初始化特定元素集中的容器或消除最令人烦恼的解析。如果您没有做任何这些事情,请使用() s。