До C++11 ключове слово auto було найменш використовуваним ключовим словом в C++. З уроку №51 ми вже знаємо, що локальні змінні мають автоматичну тривалість (створюються в точці визначення і знищуються в кінці блоку, в якому визначені).
Ключове слово auto використовувалося для того, щоб явно вказати, що змінна повинна мати автоматичну тривалість:
1 2 3 4 5 6 |
int main() { auto int boo(7); // явно вказуємо, що змінна boo типу int повинна мати автоматичну тривалість return 0; } |
Однак, оскільки всі змінні в нових версіях C++ за замовчуванням мають автоматичну тривалість, і, якщо явно не вказувати інший тип тривалості, то ключове слово auto стало зайвим і, відповідно, застарілим.
Вивід типів в C++11
У C++11 значення ключового слова auto змінилося. Розглянемо наступний стейтмент:
1 |
double x = 4.0; |
Якщо C++ і так знає, що 4.0
є літералом типу double, то навіщо нам додатково вказувати, що змінна x
повинна бути типу double? Правда, було б непогано, якби ми могли вказати змінній прийняти відповідний тип даних, ґрунтуючись на ініціалізованому значенні?
Починаючи з C++11, ключове слово auto при ініціалізації змінної може використовуватися замість типу змінної, щоб повідомити компілятору, що він повинен присвоїти тип змінній виходячи з ініціалізованого значення. Це називається виводом типу (або ще “автоматичним визначенням типу даних компілятором“). Наприклад:
1 2 |
auto x = 4.0; // 4.0 - це літерал типу double, тому і x повинен бути типу double auto y = 3 + 4; // вираз 3 + 4 оброблюється як цілочисельний, тому і змінна y повинна бути типу int |
Це навіть працює зі значеннями, що повертаються з функцій:
1 2 3 4 5 6 7 8 9 10 |
int subtract(int a, int b) { return a - b; } int main() { auto result = subtract(4, 3); // функція subtract() повертає значення типу int і, відповідно, змінна result також повинна бути типу int return 0; } |
Зверніть увагу, це працює тільки з ініціалізованими змінними. Змінні, оголошені без ініціалізації, не можуть використовувати цю особливість (оскільки немає ініціалізованого значення і компілятор не може зрозуміти, який тип даних присвоїти змінній).
Використовуючи ключове слово auto замість фундаментальних типів даних, ми не заощадимо багато часу чи зусиль, але в наступних уроках, коли типи даних будуть більш складними і довгими, ключове слово auto може дуже стати в нагоді.
Ключове слово auto і параметри функцій
Багато новачків намагаються зробити щось на кшталт наступного:
1 2 3 4 5 6 |
void mySwap(auto a, auto b) { auto x = a; a = b; b = x; } |
Це не спрацює, тому що компілятор не може визначити типи даних для параметрів функції a
і b
під час компіляції.
Якщо ви хочете створити функцію, яка буде працювати з різними типами даних, то вам краще скористатися шаблонами функцій, а не виводом типу. Це обмеження, можливо, скасують в майбутніх версіях C++ (коли auto буде використовуватися як скорочений спосіб створення шаблонів функцій), але в C++14 це не працює. Єдиний виняток — лямбда-вирази (але це вже інша тема).
Вивід типів в C++14
У C++14 функціонал ключового слова auto було розширено до автоматичного визначення типу значення, що повертається з функції. Наприклад:
1 2 3 4 |
auto subtract(int a, int b) { return a - b; } |
Так як вираз a − b
є типу int, то компілятор робить висновок, що і функція повинна бути типу int.
Хоча це може здатися зручним, але я не рекомендую так робити. Тип значення, що повертається з функції, допомагає зрозуміти викликаючому об’єкту, що саме функція повинна повертати. Якщо конкретний тип не вказано, то викликаючий об’єкт може невірно інтерпретувати тип значення, що може привести до помилок.
Так чому ж використання auto при ініціалізації змінних — це добре, а з функціями — погано? Справа в тому, що auto можна використовувати при визначенні змінної, так як значення, з якого компілятор робить висновки по типу змінної, знаходиться прямо там — в правій частині стейтменту. Однак з функціями це не так — немає контексту, який би вказував, якого типу даних повинно бути значення, що повертається. Фактично користувачеві доведеться лізти в тіло функції, щоб визначити тип значення. Отже, такий спосіб не тільки не практичний, але і більш схильний до помилок.
trailing-синтаксис в C++11
У C++11 з’явилася можливість використовувати trailing–синтаксис типу значення, що повертається (або просто “trailing-синтаксис“) — коли компілятор робить висновки про тип значення, що повертається по кінцевій частині прототипу функції. Наприклад, розглянемо наступне оголошення функції:
1 |
int subtract(int a, int b); |
У C++11 це можна записати так:
1 |
auto subtract(int a, int b) -> int; |
В цьому випадку auto не виконує вивід типу — це всього лише частина trailing-синтаксису типу значення, що повертається. Навіщо тоді це взагалі слід використовувати? Це зручно. Наприклад, можна вивести в колонку всі назви ваших функцій:
1 2 3 4 |
auto subtract(int a, int b) -> int; auto divide(double a, double b) -> double; auto printThis() -> void; auto calculateResult(int a, double x) -> std::string; |
Але цей синтаксис більш корисний в поєднанні з деякими іншими просунутими особливостями C++, такими як класи і ключове слово decltype. Ми поговоримо докладніше про інші використання auto, коли будемо розглядати ключове слово decltype.
На даний момент, я рекомендую дотримуватися традиційного (звичайного) синтаксису для визначення типу значення, що повертається з функції (без використання ключового слова auto).
Висновки
Починаючи з C++11, ключове слово auto може використовуватися замість типу змінної при ініціалізації для виконання виводу типу. У всіх інших випадках, використання ключового слова auto слід уникати, якщо на це немає вагомої причини.