【问题标题】:Why I can't initiaize a string with uniform initialization?为什么我不能用统一初始化来初始化字符串?
【发布时间】:2019-04-27 20:41:32
【问题描述】:

我有一个模拟窗口的程序;所以我将窗口的内容存储在成员数据content 中,这是一个std::string 类型:

class Window {
    using type_ui = unsigned int;
    public:
        Window() = default;
        Window(type_ui, type_ui, char);
        void print()const;

    private:
        type_ui width_{};
        type_ui height_{};
        char fill_{};
        std::string content_{};
        mutable type_ui time_{};
};

Window::Window(type_ui width, type_ui height, char fill) :
    width_{ width }, height_{ height }, fill_{ fill },
    content_{ width * height, fill } { // compile-time error here?
    //content( width * height, fill ) // works ok
}

void Window::print()const {
    while (1) {
        time_++;
        for (type_ui i{}; i != width_; ++i) {
            for (type_ui j{}; j != height_; ++j)
                std::cout << fill_;
            std::cout << std::endl;
        }
        _sleep(1000);
        std::system("cls");
        if (time_ > 10)
            return;
    }
}


int main(int argc, char* argv[]) {

    Window main{ 15, 25, '*' };
    main.print();

    std::string str{5u, '*'}; // compiles but not OK
    std::string str2(5u, '*'); // compiles and OK
    cout << str << endl; // ♣* (not intended)
    cout << str2 << endl; // ***** (ok)

    std::cout << std::endl;
}

正如您在上面看到的,我无法使用编译器抱怨“缩小类型”的curly-braces-initializer-list 初始化成员content。但它适用于“直接初始化”。

  • 为什么我不能在 Constructor-initializer-list 中使用上面的 Curly-brace-initialization-list 来调用 std::string(size_t count, char)

  • 为什么这个 std::string str{5u, '*'}; // compiles but not OK 有效但没有提供预期的输出?

  • 对我来说非常重要的是为什么相同的初始化在构造函数成员初始化列表中不起作用但在 main 中起作用(没有预期的结果)?

【问题讨论】:

    标签: c++ c++11 initializer-list stdstring


    【解决方案1】:
    • 首先,因为std::string的构造函数sts::string(size_t count, char)是显式的,所以你不能隐式调用它。

    • 第二,你没有在content{width * height, fill}中调用std::string(size_t, char),但实际上你在调用std::string(std::initializer_list&lt;char&gt;)。因此,表达式width * height 产生一个无符号整数,然后隐式转换为“相关类型”的 char,例如,您传递了Window main{ 15, 25, '*' };,它产生了(char)15 * 25 = (char)375,这是未定义的行为,因为这个值溢出了signed char。您可能会在您的机器上将“♣”或其他值作为初始化列表中的第一个元素,但它是未定义的行为,并且“”作为初始化列表中的第二个元素。因此,您正在传递 std::initializer_list{'♣', ''}。

      • 只要您调用具有多个参数的显式构造函数,就可以使用直接初始化。
    • 第二个问题的答案:“对我来说很重要的事情是为什么相同的初始化在构造函数成员初始化列表中不起作用但在 main 中起作用(没有预期的结果)?” :

    实际上它与“Constructor-member-initializer-list”没有任何关系,但实际上考虑一下:

        char c{ 127 }; // the maximum integer positive value that a signed char can hold so no narrowing conversion here. So it is OK.
    
        char c2{ 128 }; // Now 128 overflows the variavle c2. c2 is of type char and as you know it can hold positive values in range 0 to 127 and negative -1 to -128
        unsigned char uc{ 255 }; // ok because an unsigned char can hold 255
        unsigned char uc2{ 300 }; // error as the error above. An unsigned char can hold 255 as max positive value.
    
        unsigned char uc3 = 321; // ok // ok but you may get a warning. a Copy-initialization is not safe.
        cout << uc3 << endl; // you may get `A`. 321 % 256 = 65. 65 % 256 = 65. 65 in ASCII in some implementations it represents the character "A".
    

    虽然上面的uc3溢出不是好事,但结果是明确的。 (溢出一个无符号的 Xtype)。

    • 但是看看这个:

      char c3 = 321; // signed char overflows
      cout << c3 << endl; // you may get "A" and think it is correct.
      

    上面是未定义的行为。永远不要尝试溢出签名类型。

    constexpr int i = 10;
    constexpr int j = 5;
    std::string str{ i * j, 'f' }; // ok as long as the values are constexpr and don't cause any narrowing conversion this value: 10 * 5 = 50 which is a well defined signed-char value.
    
    int i = 10;
    int j = 5;
    std::string str{ i * j, 'f' }; // error. the values of i and j are not constant thus it may overflow a signed char causing narrowing conversion thus the compiler is wise enough to prevent this initialization.
    

    【讨论】:

    • 最后一个要点应该是 72 号字体。我看到很多 C++ 程序员迷恋于统一初始化(包括我自己),并且(过度/ab)不恰当地使用它(不再是我……;我已经吸取了教训)。
    猜你喜欢
    • 2012-05-17
    • 1970-01-01
    • 1970-01-01
    • 2018-03-06
    • 2012-02-05
    • 2019-04-09
    • 2012-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多