为什么我不能使用统一初始化初始化来初始化字符串?

Why I can't initiaize a string with uniform initialization?

本文关键字:初始化 字符串 不能 为什么      更新时间:2023-10-16

我有一个模拟窗口的程序;所以我将窗口的内容存储在成员数据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。但它适用于"直接初始化"。

  • 为什么我不能在构造函数初始化器列表中使用上面的大括号初始化列表来调用std::string(size_t count, char).

  • 为什么这个std::string str{5u, '*'}; // compiles but not OK有效,但给出的不是预期的ouptu?

  • 我很重要的事情是为什么相同的初始化不适用于构造函数-成员-初始化列表,而是在main中工作(没有预期的结果)?

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

  • 其次,你不是在content{width * height, fill}中调用std::string(size_t, char),但实际上你在调用std::string(std::initializer_list<char>)。因此,表达式width * height产生一个无符号的 int,然后隐式转换为"相关类型"的 char,因此例如,您传递了Window main{ 15, 25, '*' };这会产生(char)15 * 25 = (char)375这是未定义的行为,因为此值溢出了signed char。您可以在计算机上将 "♣" 或其他值作为初始值设定项列表中的第一个元素,但它是未定义的行为,而 ">" 作为初始值设定项列表中的第二个元素。因此,您传递的是 std::initializer_list{'♣', ''}。

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

事实上,它与"构造函数-成员-初始值设定项-列表"没有任何关系,但实际上考虑一下:

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.