【问题标题】:Calling constructor with "()" is different from "{}"用“()”调用构造函数与“{}”不同
【发布时间】:2017-11-29 07:38:37
【问题描述】:

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

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

【问题讨论】:

  • 用你的调试器逐步进入你的 C++ 标准库实现,亲自看看。
  • 因为它们执行不同的操作。
  • 当我在 Windows 上完成工作后启动到 Linux 时。

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


【解决方案1】:

因为std::string 有一个constructor 采用std::initializer_list,所以第一个示例将使用该构造函数创建一个包含两个字符的字符串对象。这样的初始化称为list initialization

第二个示例将创建一个包含六个字符的字符串对象,全部初始化为's'。这种初始化形式称为direct initialization

列表初始化和直接初始化可以相同,除了列表初始化禁止从较大类型到较小类型的可能转换,如果类有一个采用std::initializer_list 的构造函数,请注意这里。

【讨论】:

  • 那么,使用count, char格式直接初始化std::string的唯一方法是使用()?
  • @6EQUJ5 - 是的。列表初始化的行为是这样的,因为它的语义意味着“将这些确切的东西放入对象中”。
  • @6EQUJ5 - 这不是世界末日。
  • 直接初始化和列表初始化不是不相交的,它们是正交的。 string a{6, 's'} 既是直接又是列表。 string b = {6, 's'} 是列表但不是直接的。
  • @6EQUJ5 仅供参考,如果您使用命名变量,而不是编译器可以检查的常量,它至少会给您一个编译器错误:int var = 6; std::cout &lt;&lt; std::string{var, 's'}; 应该导致 警告: 在 {} 内缩小“var”从“int”到“char”的转换
【解决方案2】:

发生这种情况是因为在第一种情况下编译器更喜欢另一个重载并使用符号 char(6) 和 's' 初始化字符串。您可以通过将 6 更改为类似 35 的可打印字符来检查它。 虽然有用并解决了最棘手的解析问题,但 {} 构造有其注意事项。

【讨论】:

    【解决方案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 个字符组成的字符串。

    【讨论】:

      【解决方案4】:

      使用{}s 进行初始化称为列表初始化。列表初始化在几个方面遵循与正常初始化不同的规则,但这里的重要案例与std::initializer_list 有关。

      规则是:如果您要对类类型T 进行列表初始化,并且该类类型有一个采用std::initializer_list&lt;U&gt; 的构造函数,并且初始化列表中的每个元素都可以转换为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&lt;char&gt; 构造函数,但从 300char 的转换正在缩小。即使其他构造函数可以工作,也没关系,我们选择std::initializer_list&lt;char&gt; 和错误。

      但是当你写std::string(6, 's') 时,这不是列表初始化。此处考虑所有构造函数。 std::initializer_list 构造函数不匹配 - 你不能从 int 初始化 std::initializer_list&lt;char&gt; - 但第二个构造函数是,所以它被选中。这是在工作中更正常、更熟悉的重载解决方案。


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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-09
        • 2019-02-01
        • 1970-01-01
        • 2017-04-30
        • 1970-01-01
        • 1970-01-01
        • 2011-12-15
        • 2014-01-29
        相关资源
        最近更新 更多