Почему явная конструкция считается (неявным) сужающим преобразованием? - PullRequest
2 голосов
/ 20 января 2020

Рассмотрим следующий код:

uint32_t foo(uint64_t x ) {
    auto y = uint32_t { x };
    return y;
}

Это считается сужающим преобразованием, которое компилятор считает необходимым предупредить меня о (G CC 9) или даже объявить ошибку (лязг 9): GodBolt .

Мои вопросы:

  1. Почему uint32_t { x } менее явно, чем static_cast<uint32_t>(x)?
  2. Почему с клангом это более серьезно, чем с G CC, заслуживающим ошибку?

Ответы [ 3 ]

3 голосов
/ 20 января 2020

Почему uint32_t { x } менее явно, чем static_cast<uint32_t>(x)?

Это не менее явно, просто не разрешено. Сужающие преобразования не допускаются при прямой инициализации или копировании списка. Когда вы делаете auto y = uint32_t { x };, вы инициализируете прямой список y с сужающимся преобразованием. (Гарантированное исключение копирования означает, что здесь больше нет временных данных)

Почему это более серьезно с Clang, чем с G CC, заслуживающим ошибку?

Все кончено исполнители. Очевидно, Clang хочет быть более строгим и выдавать серьезную ошибку, но оба в порядке. Стандарт требует только сообщения о диагностике c, а предупреждение или ошибка покрывают это.

2 голосов
/ 20 января 2020

Добавление к ответу @ NathanOliver - предупреждений и ошибок go, если мы создадим 32-разрядное целое число следующим образом:

uint32_t foo(uint64_t x ) {
    auto y = uint32_t(x);
    return y;
}

Итак, (x) и {x} здесь не семантически эквивалентный (даже если бы тот же конструктор получал вызов, будь он классом). Гарантия отсутствия сужения в стандарте, по-видимому, применима только к инициализации списка, IIANM.

Итак, примите это как мотивацию для использования инициализации в фигурных скобках, если вы хотите быть очень осторожными (или скобки, если вы хочу, чтобы тебя не беспокоили.)

1 голос
/ 20 января 2020

С https://en.cppreference.com/w/cpp/language/list_initialization:

Сужающие преобразования

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

...

-преобразование из целочисленного или не перечисленного типа перечисления в целочисленный тип, который не может представлять все значения оригинала, за исключением случаев, когда источником является константное выражение, значение которого может храниться точно в тип цели

Похоже, что clang здесь более совместим, чем g cc (хотя имейте в виду, что я не адвокат по языкам) *: стандарт обязывает вас, если вы используете списки инициализаторов, вы нет опасности сужения конверсии. Это сознательный выбор дизайна для исправления довольно беспорядочного неявного преобразования, встроенного в язык, и, по общему признанию, ясный способ, которым вы произносите это в своем примере, является сопутствующим раздражением.

Редактировать: * и это не так займет много времени - кажется, «не разрешено» в cppreference переводится как «зависит от реализации» в стандарте, согласно ответу NathanOliver. Вот что я получаю за то, что не проверял источник.

...