Макросы нужны там где нужно во время компиляции программы генерировать код. Функции или даже шаблоны для этого не подходят.
Например вы хотите логгер, который бы писал в каком файле, и на какой строке произошла ошибка, вы пишите код
cout << __FILE__ << __LINE__ << "error message";
И все хорошо пока вы его вставляете руками туда куда нужно. Но вот вы решили что это некрасиво, и планируете вынести код в функцию. Однако - план проваливается, так как теперь код пишет каждый раз один и тот же файл, и одну и ту же строку - ту самую в которой код удобно расположился в вашей функции. Выходов два:
- каждый раз дублировать этот код
- использовать макросы
Итак теперь вы тяжело вздохнув, и закрыв портретик Страуструпа шторкой ( чтобы не видел как вы предаетесь адскому макросостроению ) пишите макрос
#define LOG(x) std::cout << __FILE__ << __LINE__ << x << std::endl;
Теперь где угодно можно писать
LOG("this is error number " << errno);
и все будет работать как и было запланировано.
Еще из полезных свойств макросов - улучшать чужой плохой код. Если у вас есть много переменных, или переменных имена у которых почти совпадают с именами функций и вам нужно сделать этот код умнее, вы пользуетесь макросами, склеиваете строки через ## или преобразовываете переменную в строку через #
Это довольно мощные инструменты ( пусть до лисповских максросов им далеко ) которые реально позволяют улучшить код и/или избежать кучу повторений одного и того же блока кода.