чи не вдалося досягти того самого результату без явного оголошення змінної auto
?
Я збираюся переформулювати ваше запитання таким чином, щоб допомогти вам зрозуміти, для чого вам потрібно auto
:
Чи не вдалося досягти того самого результату без явного використання заповнювача типу ?
Хіба це не було можливо ? Звичайно, це було "можливо". Питання в тому, чи варто було б докладати зусиль для цього.
Більшість синтаксисів інших мов, що не вводять імена, працюють одним із двох способів. Існує спосіб Go-like, де name := value;
оголошується змінна. І є спосіб, подібний до Python, де name = value;
оголошує нову змінну, якщо name
вона раніше не була оголошена.
Припустимо, що жодних синтаксичних проблем із застосуванням того чи іншого синтаксису до C ++ не існує (хоча я вже бачу, що identifier
після цього :
в C ++ означає «зробити мітку»). Отже, що ви втрачаєте порівняно із заповнювачами?
Ну, я більше не можу цього робити:
auto &name = get<0>(some_tuple);
Дивіться, auto
завжди означає "цінність". Якщо ви хочете отримати посилання, вам потрібно явно використовувати &
. І він по праву не зможе скомпілювати, якщо вираз призначення є першим значенням. Жоден із синтаксисів на основі присвоєння не має можливості розрізнити посилання та значення.
Тепер ви можете змусити такі синтаксиси призначення виводити посилання, якщо задане значення є посиланням. Але це означало б, що ви не можете зробити:
auto name = get<0>(some_tuple);
Це копіює з кортежу, створюючи об’єкт, незалежний від some_tuple
. Іноді це саме те, що ви хочете. Це ще корисніше, якщо ви хочете перейти від кортежу з auto name = get<0>(std::move(some_tuple));
.
Добре, можливо, ми могли б трохи розширити ці синтаксиси, щоб врахувати цю відмінність. Можливо, &name := value;
або &name = value;
означало б вивести посилання типу auto&
.
Добре, гаразд. Як що до цього:
decltype(auto) name = some_thing();
О, це правильно; С ++ насправді має два заповнювачі: auto
іdecltype(auto)
. Основна ідея цього відрахування полягає в тому, що він працює точно так, як якщо б ви робили це decltype(expr) name = expr;
. Отже, у нашому випадку, якщо some_thing()
це об’єкт, він виведе об’єкт. Якщо some_thing()
це посилання, воно виведе посилання.
Це дуже корисно, коли ви працюєте з кодом шаблону і не впевнені, яким саме буде повернене значення функції. Це чудово підходить для пересилання, і це важливий інструмент, навіть якщо він не використовується широко.
Тож тепер нам потрібно додати більше до нашого синтаксису. name ::= value;
означає "робити те, що decltype(auto)
робить". У мене немає еквівалента для пітонічного варіанту.
Дивлячись на цей синтаксис, чи не так легко випадково ввести неправильний тип? Мало того, навряд чи це самодокументування. Навіть якщо ви ніколи раніше цього не бачили decltype(auto)
, він досить великий і очевидний, що ви можете принаймні легко сказати, що відбувається щось особливе. Тоді як візуальна різниця між ::=
і :=
мінімальна.
Але це думка; є більше предметних питань. Дивіться, все це базується на використанні синтаксису присвоєння. Ну ... як щодо місць, де ви не можете використовувати синтаксис призначення? Подобається це:
for(auto &x : container)
Ми змінюємо це на for(&x := container)
? Тому що, схоже, це говорить про щось дуже різне від діапазону for
. Схоже, це оператор ініціалізатора із звичайного for
циклу, а не на основі діапазону for
. Це також був би інший синтаксис, ніж невиведені випадки.
Крім того, ініціалізація копіювання (з використанням =
) - це не те саме в C ++, що і пряма ініціалізація (з використанням синтаксису конструктора). Тому name := value;
може не спрацювати у тих випадках, коли auto name(value)
б мали.
Звичайно, ви могли б оголосити, що :=
використовуватимете пряму ініціалізацію, але це було б досить суперечливим чином поведінки решти С ++.
Крім того, є ще одна річ: C ++ 14. Це дало нам одну корисну функцію вирахування: вирахування типу повернення. Але це базується на заповнювачах. Так само, як і на основі діапазону for
, він заснований на імені типу, яке заповнює компілятор, а не якимсь синтаксисом, застосованим до певного імені та виразу.
Дивіться, усі ці проблеми походять з одного джерела: ви вигадуєте абсолютно новий синтаксис для оголошення змінних. Деклараціям на основі заповнювачів не потрібно було вигадувати новий синтаксис . Вони використовують точно такий самий синтаксис, як і раніше; вони просто використовують нове ключове слово, яке діє як тип, але має особливе значення. Саме це дозволяє йому працювати в залежності від діапазону for
та для вирахування типу повернення. Саме це дозволяє йому мати кілька форм ( auto
проти decltype(auto)
). І так далі.
Заповнювачі працюють, оскільки вони є найпростішим рішенням проблеми, одночасно зберігаючи всі переваги та загальність використання фактичної назви типу. Якщо ви придумали іншу альтернативу, яка працювала б так універсально, як заповнювачі, навряд чи вона була б такою простою, як заповнювачі.
Хіба що це було просто написання заповнювачів з різними ключовими словами чи символами ...