Ответ
Аргументы макроса и всё его тело необходимо заключать в скобки, чтобы гарантировать корректный порядок вычислений после подстановки. Макросы работают на уровне текстовой замены в препроцессоре, не понимая синтаксиса или приоритета операторов C++. Без скобок это приводит к тонким и опасным ошибкам.
Пример проблемы:
#define MULTIPLY_UNSAFE(a, b) a * b
int result1 = MULTIPLY_UNSAFE(2 + 3, 4); // Подстановка: 2 + 3 * 4 = 14 (ожидалось 20)
#define SQUARE_UNSAFE(x) x * x
int result2 = SQUARE_UNSAFE(++value); // Подстановка: ++value * ++value. Неопределённое поведение! Инкремент дважды.
Правильное решение:
#define MULTIPLY_SAFE(a, b) ((a) * (b))
int result3 = MULTIPLY_SAFE(2 + 3, 4); // Подстановка: ((2 + 3) * (4)) = 20
#define SQUARE_SAFE(x) ((x) * (x))
// Но даже с скобками SQUARE_SAFE(++value) останется проблемой из-за двойного инкремента.
// Макросы с побочными эффектами в аргументах — это антипаттерн.
Ключевые правила:
- Каждый аргумент в теле макроса заключайте в круглые скобки:
(a),(b). - Всё выражение макроса заключайте в круглые скобки:
((a) + (b)). - Избегайте передачи в макросы выражений с побочными эффектами (инкремент, вызов функций). Для таких случаев в современном C++ предпочтительнее использовать
inline-функции илиconstexpr.
Сравнение с inline-функцией (рекомендуемый подход в C++):
template<typename T>
inline T multiply_safe(T a, T b) {
return a * b;
}
// Нет проблем с приоритетом, есть проверка типов, аргументы вычисляются ровно один раз.
int result = multiply_safe(2 + 3, 4); // OK, возвращает 20