为什么我的可变参数宏不能正确接受任何参数?

Why won't my variadic macro accept no arguments correctly?

Overloading Macro on Number of Arguments

https://codecraft.co/2014/11/25/variadic-macros-tricks/

我一直在查看上面的两个 link,试图让下面的代码工作:

#define _GET_NUMBER(_0, _1, _2, _3, _4, _5, NAME, ...) NAME
#define OUTPUT_ARGS_COUNT(...) _GET_NUMBER(_0, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0)

...

cout << OUTPUT_ARGS_COUNT("HelloWorld", 1.2) << endl;
cout << OUTPUT_ARGS_COUNT("HelloWorld") << endl;
cout << OUTPUT_ARGS_COUNT() << endl;

编译、运行并给出以下输出:

2
1
1

我一辈子都弄不明白为什么调用 OUTPUT_ARGS_COUNT() 给我的是 1 而不是 0。我对我要使用的代码有很好的理解,但它是对我来说还是有点希腊语,所以我想我可能没有正确应用某些东西,尽管我从字面上复制并粘贴了堆栈溢出时 link 中的示例代码。

我正在使用 g++ 5.4.0 20160609 进行编译。

如果您能向我指出任何想法或其他资源,我们将不胜感激。

你可以在http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html看到:

Second, the ‘##’ token paste operator has a special meaning when placed between a comma and a variable argument. If you write

#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

and the variable argument is left out when the eprintf macro is used, then the comma before the ‘##’ will be deleted. This does not happen if you pass an empty argument, nor does it happen if the token preceding ‘##’ is anything other than a comma.

eprintf ("success!\n")
 → fprintf(stderr, "success!\n");

The above explanation is ambiguous about the case where the only macro parameter is a variable arguments parameter, as it is meaningless to try to distinguish whether no argument at all is an empty argument or a missing argument. CPP retains the comma when conforming to a specific C standard. Otherwise the comma is dropped as an extension to the standard.

因此,(除非使用适当的扩展名)OUTPUT_ARGS_COUNT() 被算作 1 个空参数(逗号与 ##__VA_ARGS__ 保持一致)。

C 标准规定

If the identifier-list in the macro definition does not end with an ellipsis, [...]. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...)

(C2011 6.10.3/4;已强调)

C++11 在第 16.3/4 段中包含具有相同效果的语言。

那么,在这两种情况下,如果您的宏调用被解释为具有零参数,那么您的程序将是不合格的。另一方面,预处理器确实识别并支持空宏参数——即由零个预处理标记组成的参数。那么,原则上,这里在没有参数和单个空参数之间存在歧义,但在实践中,只有后一种解释才能产生符合要求的程序。

g++ 选择后一种解释(另一个答案引用了它的文档)因此是合理和适当的,但如果您希望代码可移植,依赖它是不安全的。采用替代解释的编译器会有不同的行为,可能通过提供您期望的行为,但也可能通过拒绝代码。