По разным причинам - или средствами языка нельзя что-то емко выразить (как в либах Unreal Engine), или для производительности (иногда голый код лучше оптимизируется чем функции, и макросы пишутся вместо функций под комментарием "счастливого дебага, тварь"), иногда просто по дурости (как в QT).
И там можно выстрелить себе в ногу самым неожиданным образом.
Вот простой макрос:
#define INCREMENT_BOTH(x, y) x++; y++
Что тут не так?
Да много чего.
Вот такой пример:
int a = 0; int b = 0;
INCREMENT_BOTH(a+b, b);
Вообще это преобразуется в
a+b++; b++;
И это явно не то, что мы хотели. Мы хотели тут ошибку компиляции на том, что "пытаемся инкрементить lvalue", а вместо этого простой ворнинг про "неиспользуемый результат".
Но это полбеды, тут даже идейно макрос использован неверно.
Какой-нибудь вот такой пример:
int main() {
int a = 0; int b = 0;
for (const auto f: {true, false}) {
if (f)
INCREMENT_BOTH(a, b);
}
std::cout << "a: " << a << "\n"
<< "b: " << b << "\n"
<< std::endl;
return 0;
}
Разумно выведет
Program returned: 0
a: 1
b: 2
а clang даже со включенным Wall не выдаст ни одного ворнинга.
И это далеко не все проблемы. Когда-то давно, когда мне надо было писать макросы, мне к задачам выдали 18-страничный гайд "как правильно писать макросы", согласно которому единственно верным способом написать такой макрос будет
#define INCREMENT_BOTH(x, y) \
do { \
(x)++; \
(y)++; \
} while (0)
Не используйте макросы без крайней необходимости. А если используете - найдите тот гайд, и пришлите его мне (я его потерял и очень об этом жалею 🙁 )