Инициализация: =, () и {}
Значение, которое переменная получает при объявлении, называют начальным, а сам этот шаг — инициализацией. Это то, что кладут в коробку в тот момент, когда наклеивают на неё имя. Если значение не подходит к типу переменной, компилятор сам его преобразует, и часть значения может пропасть:
#include <iostream>
int main() {
double price = 2.9;
int whole = price;
std::cout << whole << '\n'; // 2
}int хранит только целые числа, поэтому дробная часть 0.9 отбрасывается — без округления. Это похоже на коробку для целых плиток: от плитки 2.9 отламывают кусок 0.9, и в коробку ложится 2. Компилятор не сообщает ни об ошибке, ни о предупреждении, и программа печатает 2.
Три формы записи
Начальное значение можно записать тремя способами:
int a = 5;
int b(5);
int c{5};
std::cout << a << ' ' << b << ' ' << c << '\n'; // 5 5 5int a = 5;— форма со знаком=, привычная и самая частая;int b(5);— форма с круглыми скобками;int c{5};— форма с фигурными скобками.
Для int и 5 все три строки делают одно и то же. Различаются они тем, как обращаются с неподходящим значением: = и круглые скобки его преобразуют, как в примере выше.
Пустые фигурные скобки задают ноль: int total{}; значит то же, что int total{0};. Чем фигурные скобки лучше двух других форм, показывает следующий пример.
Скобки против сужения
Преобразование, при котором может пропасть часть значения, называют сужающим. Так бывает в двух случаях:
- дробное число попадает в целый тип;
- число не помещается в новый тип.
Важен не размер типа в байтах, а то, может ли пропасть часть значения. Фигурные скобки сужение запрещают:
int count{2.5}; // ✗error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]Это ошибка компиляции: программа не соберётся, пока значение не исправят.
Целую константу, которая точно помещается в новый тип, скобки пропускают: double ratio{2}; компилируется. А дробное значение в целый тип скобки не пропускают никогда, даже 2.0: int n{2.0}; тоже не скомпилируется. Поэтому сужением будет и int whole{price}; из первого примера.
Фигурные скобки — самая строгая из трёх форм: ошибку с типом они находят сразу. Этот вопрос часто задают на собеседовании: чем int x{2.5}; отличается от int x = 2.5;.
Тип из начального значения
Вместо типа можно написать auto: тогда компилятор выведет тип из начального значения, будто сам поставит штамп на коробку по тому, что в неё положили. Тип совпадает с типом литерала:
auto count = 10;даётint;auto price = 2.5;даётdouble;auto ratio = 2.5f;даётfloat.
Тип берётся из начального значения, поэтому без него auto не работает: auto total; не скомпилируется.
С auto можно незаметно получить не тот тип. Если дробную сумму начать с auto total = 0;, литерал 0 даст int. Тогда присваивание total = total + 2.5; сохранит в total только 2, а компилятор промолчит. Нужен литерал подходящего типа: auto total = 0.0;.
Частая ошибка
Переменную объявили без начального значения, а потом прочитали:
int total;
total = total + 5; // ✗warning: variable 'total' is uninitialized when used here [-Wuninitialized]Если переменную внутри функции объявили без начального значения, её значение не определено. Это та самая коробка, в которой лежит непредсказуемое значение. Чтение такого значения — неопределённое поведение: стандарт C++ не говорит, что сделает программа. Компилятор только предупреждает, поэтому задавайте значение сразу: int total{0}; или int total{};.
Главное
- Инициализация задаёт начальное значение;
=и круглые скобки молча преобразуют неподходящее значение, и дробная часть пропадает. - Фигурные скобки запрещают сужение:
int n{2.0};иint whole{price};не скомпилируются, аdouble ratio{2};— да. autoберёт тип из начального значения:auto total = 0;— этоint.- Переменную без начального значения читать нельзя, поэтому задавайте значение сразу, например
int total{};.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.