Почему я не могу инициализировать строку с равномерной инициализацией? - PullRequest
4 голосов
/ 27 апреля 2019

У меня есть программа, которая имитирует окно; Таким образом, у меня есть содержимое окна, хранящееся в элементе данных 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;
}

Как вы можете видеть выше, я не смог инициализировать элемент content с curly-braces-initializer-list, на что компилятор жалуется на "тип сужения". Но это работает с «прямой инициализацией».

  • Почему я не могу использовать список Curly-brace-initialization-list выше в Constructor-initializer-list для вызова std::string(size_t count, char).

  • Почему это std::string str{5u, '*'}; // compiles but not OK Работает, но дает не предназначенный Optu?

  • Для меня очень важно то, что такая же инициализация не работает в списке-конструкторе-инициализации-списке, но работает в main (с непредвиденным результатом)?

1 Ответ

8 голосов
/ 27 апреля 2019
  • Во-первых, поскольку конструктор std::string sts::string(size_t count, char) является явным, поэтому вы не можете вызвать его неявно.

  • Во-вторых, вы не вызываете std::string(size_t, char) in content{width * height, fill}, но на самом деле вы вызываете std::string(std::initializer_list<char>).Таким образом, выражение width * height возвращает беззнаковое целое, а затем неявно преобразуется в тип char, который является «связанным типом», таким образом, например, вы передали Window main{ 15, 25, '*' };, что дает (char)15 * 25 = (char)375, что является неопределенным поведением, поскольку это значение переполняет signed char.Вы можете получить на своем компьютере «♣» или другие значения в качестве первого элемента в списке инициализаторов, но это неопределенное поведение и «» в качестве второго элемента в списке инициализаторов.Таким образом, в результате вы передаете std :: initializer_list {'♣', ''}.

    • Используйте прямую инициализацию, если вы вызываете явный конструктор, имеющий более одного параметра.
  • Ответ на второй вопрос: «Для меня очень важно то, что одна и та же инициализация не работает для constructor-member-initialization-list, но работаетв основном (с непредвиденным результатом)? ":

На самом деле это не имеет никакого отношения к" 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 выше не очень хорошо, но результат хорошо определен.(переполняет неподписанный X-тип).

  • Но посмотрите на это:

    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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...