1. 基础语法
定义格式:
#define MACRO_NAME(fixed_args, ...) macro_body
#define LOG(fmt, ...) printf(fmt, __VA_ARGS__)
LOG("Value: %d, Name: %s", 42, "Alice"); // 展开为 printf("Value: %d, Name: %s", 42, "Alice")
1)… 表示可变参数列表。
2)在宏体中,用 VA_ARGS 展开所有可变参数。
2. 处理空的可变参数
当可变参数为空时,直接使用 __VA_ARGS__
可能导致语法错误(如尾随逗号)。可通过 ##
运算符优化:
使用 ##__VA_ARGS__
:
#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
LOG("Hello"); // 展开为 printf("Hello")(无尾随逗号)
##
在可变参数为空时,自动删除前面的逗号。
3. C++20 的 VA_OPT(可选展开)
C++20 引入 __VA_OPT__
,可更灵活地控制可变参数的展开:
#define LOG(...) printf("Message: " __VA_ARGS__ __VA_OPT__(,) "\n")
当可变参数非空时,__VA_OPT__(,)
展开为 ,
;为空时则不展开。
LOG("Error: %d", 404); // 展开为 printf("Message: " "Error: %d", 404 "\n")
LOG(); // 展开为 printf("Message: " "\n")
4. 示例
(1) 调试日志
#define DEBUG(...) fprintf(stderr, "[DEBUG] " __VA_ARGS__)
DEBUG("File: %s, Line: %d\n", __FILE__, __LINE__);
(2) 泛型包装
#define CALL_FUNC(func, ...) func(__VA_ARGS__)
CALL_FUNC(printf, "Answer: %d", 42); // 展开为 printf("Answer: %d", 42)
(3) 条件编译
#ifdef VERBOSE
#define LOG(...) printf(__VA_ARGS__)
#else
#define LOG(...) (void)0
#endif
5. 注意事项
- 参数中的逗号:
若参数包含未保护的逗号(如模板类型),需用括号包裹:
LOG("Types: %s", (std::vector<int>, std::map<std::string, float>));
-
兼容性:
##__VA_ARGS__
是 GNU 扩展,但主流编译器(GCC、Clang、MSVC)均支持。
__VA_OPT__
是 C++20 标准特性,需编译器支持。 -
避免副作用:
宏展开可能导致多次参数求值:
#define SQUARE_SUM(x, ...) ((x)*(x) + SQUARE_SUM(__VA_ARGS__))
// 错误:递归展开可能导致无限循环
#include <cstdio>
// 可变参数宏:支持格式化字符串和可变参数
#define LOG(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__)
int main() {
LOG("Start"); // 输出: [INFO] Start
LOG("Sum: %d", 3 + 5); // 输出: [INFO] Sum: 8
return 0;
}