Понимание того, когда происходят проверки в () и {} инициализации через конструкторы классов

Насколько я понимаю, {} — это способ инициализации переменных с некоторыми преимуществами «безопасности» по сравнению с другими методами, такими как запрет сужения:

int some_int_a = 1.2;  // narrows
int some_int_b (1.2);  // narrows
int some_int_c {1.2};  // does NOT compile, cannot narrow

Все идет нормально. Я недавно обнаружил, что не совсем понял, когда происходит такая проверка? Например, в следующем коде:

#include <iostream>

class ExClass{
    private:
        const int i;
    public:
        ExClass(int i=0): i{i} {
            // needed even if empty
        }
        void print(void){
            std::cout << "const int i = " << i << std::endl;
        }
};

int main(void)
{

    ExClass ex_a (2.3);  // narrows! this was surprising to me, I (probably naively)
                         // expected i{i} in the constructor to forbid this.
    ex_a.print();

    ExClass ex_b {2.3};  // does not compile
    ex_b.print();

    return 0;
}

Я предполагаю, что это означает, что в случае ex_a сначала создается промежуточное звено int полностью с сужающим преобразованием, затем это промежуточное звено int используется для инициализации скобками i в конструкторе. Хотя во втором случае промежуточное звено int не может быть инициализировано скобками с конфликтующим вводом, верно?

Есть ли способ написать вещи таким образом, чтобы не было «промежуточного» сужения, чтобы инициализация скобки класса обнаруживала ошибочный ввод?

Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
3
0
107
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Отладчик, вероятно, покажет вам это очень быстро! Посмотрите, что вы делаете: if (ExClass(next) == false || open_brackets_stack.empty() == false)

Что такое тип ExClass, когда вы пытаетесь сравнить его с закрытой скобкой

Подсказка: подумайте о том, когда вы создаете каждый новый экземпляр скобки. Они все в скобках? Они все открытые скобки?

Не уверен, о чем вы говорите.

macroland 18.12.2020 17:26
Ответ принят как подходящий

Здесь происходит то, что конструктор:

ExClass(int i=0): i{i}

Принимает параметр int и сужает здесь:

ExClass ex_a (2.3);

Это хорошо. Когда вы находитесь в конструкторе, i становится int (параметром, а не элементом). Следовательно, в i{i} нет сужения (это int к int). Вы получаете ошибку, когда вы меняете свой конструктор на:

ExClass(double i=0): i{i} { }

Потому что сейчас действительно происходит сужение i{i}.

Обратите внимание, что gcc выдает предупреждение с настройками по умолчанию и должен -pedantic-errors распознавать его как ошибку. Спасибо Реми за указание на то, что на самом деле это ошибка, а не предупреждение.

Спасибо, так что это подтверждает то, что я думал. Знаете ли вы, есть ли (материнский) способ избежать первого сужения, т.е. передать инициализацию конструктору "насквозь", без промежуточного сужения?

Zorglub29 18.12.2020 17:55

@ Zorglub29 "промежуточного сужения" нет, сужение происходит здесь ExClass ex_a (2.3); и здесь ExClass ex_b {2.3}; и далее все целые числа и в первом случае сужение разрешено, а во втором нет. Почему при откладывании сужения на список инициализаторов (как в моем примере) появляется только предупреждение а не ошибка я не знаю

463035818_is_not_a_number 18.12.2020 17:59

Звучит как ошибка компилятора или, по крайней мере, отговорка. Согласно cppreference (здесь и здесь ), сужающие преобразования не разрешены в скобочном списке инициализации, даже если они используются в списке инициализации члена конструктора. Поэтому i{i} не должен компилироваться при инициализации int с помощью double (см. этот ответ). Однако GCC позволяет это. Но clang не делает («ошибка: тип« double »не может быть сужен до« int »в списке инициализаторов»)

Remy Lebeau 18.12.2020 20:28

@RemyLebeau Спасибо. Еще один случай, когда значения по умолчанию gccs немного странные. С -pedantic-errors выдает ошибку.

463035818_is_not_a_number 18.12.2020 20:46

Ааа, так ты имеешь в виду @RemyLebeau, что я был прав, когда удивился? ^^

Zorglub29 18.12.2020 20:55

@ Zorglub29 Zorglub29 проблема с предупреждением / ошибкой связана с примером замены вашего конструктора на ExClass(double i=0): i{i} { }. В вашем () случае не должно быть никакой ошибки

463035818_is_not_a_number 18.12.2020 20:56

Инициализация фигурной скобки (начиная с C++11) предотвращает сужение. Как сообщает cppreference в разделе «Сужение конверсий»:

list-initialization ограничивает разрешенные неявные преобразования запрещающие следующее: преобразование из типа с плавающей запятой в целочисленный тип преобразование из long double в double или в float и преобразование из double в float, за исключением случаев, когда источником является константное выражение и переполнения не происходит преобразование из целочисленного типа в тип с плавающей запятой, за исключением случаев, когда источником является константное выражение, значение которого может быть сохранено точно в целевом типе преобразование из целочисленного типа или типа перечисления с незаданной областью в целочисленный тип, который не может представлять все значения оригинала, за исключением случаев, когда source — это константное выражение, значение которого может храниться точно в тип цели преобразование из типа указателя или типа указателя на член в bool (начиная с C++20)

Поэтому предпочтите инициализацию фигурной скобки альтернативам.

Да, меня удивляет то, как произошло «промежуточное» сужение...

Zorglub29 18.12.2020 17:53
Есть ли способ написать вещи таким образом, чтобы не было «промежуточного» сужения, чтобы инициализация скобки класса обнаруживала ошибочный ввод?

Вы можете заставить конструктор принимать параметр шаблона, чтобы он принимал тип того, что передает вызывающая сторона, например:

#include <iostream>

class ExClass{
    private:
        const int i;
    public:
        template<typename T>
        ExClass(T i=T{}): i{i} {
            // needed even if empty
        }
        void print(void){
            std::cout << "const int i = " << i << std::endl;
        }
};

int main(void)
{

    ExClass ex_a (2.3);  // should not compile!
    ex_a.print();

    ExClass ex_b {2.3};  // should not compile!
    ex_b.print();

    return 0;
}

К сожалению, GCC разрешает это сужение по умолчанию, выдавая предупреждение, а не ошибку (используйте -pedantic-errors, чтобы вызвать ошибку):

предупреждение: сужение преобразования «i» из «double» в «int» [-Wnarrowing]

Однако Clang выдает ошибку:

ошибка: тип 'double' не может быть сужен до 'int' в списке инициализаторов [-Wc++11-narrowing]

Это выглядит очень красиво, большое спасибо. Таким образом, это компромисс между тем, чтобы сделать вещи более безопасными, и ввести немного дополнительной магии :).

Zorglub29 18.12.2020 21:09

Другие вопросы по теме