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] StartLOG("Sum: %d", 3 + 5); // 輸出: [INFO] Sum: 8return 0;
}