Українська
Перевантаження та результати
Перевантаження і параметри за замовчуванням
Перевантаження (overloading) дозволяє кільком функціям мати однакове ім’я, якщо відрізняються їхні параметри. Наприклад, area(double) може обчислювати площу круга, а area(double, double) – прямокутника. Компілятор обирає відповідну функцію за кількістю та типами аргументів, а не за назвами змінних у виклику. Тип результату сам по собі не дає можливості створити інше перевантаження.
Оголошення double area(double); і int area(double); суперечать одне одному. Пара f(int) і f(int&) може бути оголошена, проте для звичайної змінної int виклик здатен бути неоднозначним: обидва кандидати підходять. Не створюйте такі набори без вагомої причини. Точний збіг типів зазвичай кращий за перетворення, але детальні правила потрібно читати в https://learn.microsoft.com/cpp/cpp/function-overloading.
Аргумент за замовчуванням підставляється, коли виклик не містить відповідного аргументу. Такі параметри розташовують наприкінці списку. Значення задають в одному видимому оголошенні, а не повторюють у кожному визначенні. Не поєднуйте f(int) та f(int,int=0): виклик f(1) не має очевидного єдиного кандидата. Зручність інтерфейсу не повинна створювати неоднозначність.

Рис. 3.3. Підказка параметрів перевантажених функцій
Приклад 2. Геометрія фігур
Обидві функції area обчислюють площу, але приймають різні набори розмірів. cost має типовий тариф. Дані в цьому прикладі сталі та допустимі; у програмі з введенням потрібно перевірити додатність і скінченність розмірів перед викликом.
cpp
#include <print>
#include <numbers>
[[nodiscard]] double area(double radius)
{
return std::numbers::pi * radius * radius;
}
[[nodiscard]] double area(double length, double width)
{
return length * width;
}
double cost(double square, double rate = 250.0)
{
return square * rate;
}
int main()
{
std::println("Circle: {:.2f}", area(2.0));
std::println("Room: {:.2f}", area(5.0, 4.0));
std::println("Cost: {:.2f}", cost(area(5.0, 4.0)));
std::println("Other rate: {:.2f}", cost(20.0, 300.0));
}text
Circle: 12.57
Room: 20.00
Cost: 5000.00
Other rate: 6000.00Атрибут [[nodiscard]] просить діагностувати випадок, коли результат просто проігноровано. Він не змушує користувача перевірити зміст значення і не замінює тестування. Вкладений виклик cost(area(...)) спочатку отримує площу й передає її іншій функції; округлення до двох цифр відбувається лише при виведенні, а не між обчисленнями.
Повернення кількох значень
std::pair об’єднує два значення, std::tuple – довільну скінченну кількість. Їхні заголовки – <utility> і <tuple>. Структурована прив’язка (structured binding) auto [low, high] = result; дає змістовні локальні імена компонентам. Проте велика кількість компонентів без назв ускладнює підтримку: читач має пам’ятати порядок.
Простий struct є агрегатом із полями, наприклад struct Summary { int minimum; int maximum; double mean; };. Повернення Summary виражає зміст через імена. У цій темі структура лише групує дані; конструктори, інкапсуляція та методи будуть розглянуті в темі класів. Доступ result.mean читається як «поле mean об’єкта result».
Приклад 3. Статистика трьох спостережень
Функція обчислює мінімум, максимум і середнє без зміни аргументів. Для компактної серії передаємо три значення; масив довільної довжини стане природним узагальненням у темі 4.
cpp
#include <print>
#include <algorithm>
struct Summary
{
double minimum;
double maximum;
double mean;
};
Summary summarize(double a, double b, double c)
{
return {std::min({a, b, c}), std::max({a, b, c}),
a / 3.0 + b / 3.0 + c / 3.0};
}
int main()
{
const auto [low, high, mean] = summarize(3.0, 7.0, 5.0);
std::println("min={:.1f}; max={:.1f}; mean={:.1f}",
low, high, mean);
}text
min=3.0; max=7.0; mean=5.0Поділ кожного доданка до підсумовування зменшує ризик переповнення проміжної суми великих додатних значень, але не є універсальним чисельно точним алгоритмом. Для цих навчальних скінченних малих даних він достатній. Структурована прив’язка без & створює власне значення результату; для запозичення об’єкта потрібна інша форма та контроль його часу життя.
Час життя, static та constexpr
Час життя (lifetime) визначає, коли об’єкт існує. Локальна автоматична змінна зникає при виході зі свого блока. Тому не можна повертати посилання на локальну змінну: викликач отримає посилання на об’єкт, життя якого вже закінчилося. Повернення числа або невеликої структури за значенням є природним безпечним рішенням.
Локальна static змінна зберігається між викликами, але її ім’я доступне лише в області оголошення. Лічильник static int calls = 0; у функції пам’ятає попередні виклики. Це корисно для спеціальних задач, але створює прихований стан: два однакові виклики можуть залежати від попередньої історії. Для тестованого обчислення краще передавати потрібний стан явно. Глобальні змінні посилюють цю проблему.
inline у сучасному C++ стосується насамперед правил визначення в кількох одиницях трансляції. Він не наказує компілятору обов’язково вставити тіло замість виклику. Оптимізатор може вбудувати функцію без цього слова або не вбудувати позначену ним. Визначення невеликої inline функції в заголовку дозволяє використовувати його з кількох .cpp, дотримуючись правил однакових визначень.
constexpr функція може виконуватися під час компіляції, якщо аргументи й контекст це дозволяють; зі звичайними введеними аргументами вона виконується під час роботи. static_assert(condition) перевіряє умову під час компіляції і не генерує запит до користувача. Це зручно для відомих математичних властивостей, але не перевіряє майбутні значення з клавіатури. Функція з типом результату auto дозволяє виведення типу з return; для рекурсії явний тип часто читабельніший і уникає проблем до завершення виведення типу.