这是因为 MSVC 预处理器缺少 Argument Prescan 功能还是 MSVC 违反了某些 c++ 标准?
这是我在 Microsoft 中拼凑起来的最佳工作方式。
1.宏定义:
#define _EXCLUDE_FIRST_ARG_(first, ...) __VA_ARGS__
#define EXCLUDE_FIRST_ARG(...) _EXCLUDE_FIRST_ARG_(__VA_ARGS__)
...显然要定义宏。
2。调用
EXCLUDE_FIRST_ARG(a, b, c, d, e)
MS 的预处理器识别出一个类似函数的宏 EXCLUDE_FIRST_ARG 已定义,因此开始扩展:
2a。参数识别
宏定义有形参...;它使用参数a, b, c, d, e 调用。这是一个没有命名参数的可变参数,因此所有参数都绑定到可变参数。
2b。参数替换
__VA_ARGS__ 在EXCLUDE_FIRST_ARG 的替换列表中被提及。它没有被字符串化或参与粘贴。这使它有资格进行参数替换。替换参数前,表达式:
a, b, c, d, e
...由预处理器评估。这些没有什么特别的(没有进行类似函数的宏调用;并且没有一个参数是类似对象的宏),因此评估是相同的;也就是说,评估的参数是:
a, b, c, d, e
根据参数替换规则,替换替换列表中的参数“名称”。一旦发生这种情况,我们就有了:
_EXCLUDE_FIRST_ARG_(a, b, c, d, e)
到目前为止,所有其他主要预处理器都在做同样的事情。但是 MS 的预处理器在扩展 __VA_ARGS__ 时表现出一种特殊的行为;具体来说,特殊之处在于整个替换只是一个标记。您可能会在该扩展中看到逗号;我也是;但对于 MS 来说,它仍然只是一个令牌。
2[在 b 和 c 之间]。字符串化和粘贴,不分先后
这只是此帐户中的语义占位符。这里是这些操作发生的地方,但由于我们没有做任何事情,在这种情况下什么都不会发生。
2c。重新扫描并进一步更换
在这一步,生成的替换列表本身:
_EXCLUDE_FIRST_ARG_(a, b, c, d, e)
...重新扫描以获取更多宏。在此重新扫描期间,预处理器注意到对象类宏 _EXCLUDE_FIRST_ARG_ 正在使用“参数”a, b, c, d, e 调用(对于其他主要预处理器,这是五个参数)。
2c.a.参数识别
_EXCLUDE_FIRST_ARG_ 有一个命名参数first 和一个可变参数。 注意: 在 C++20 之前的预处理器中,从技术上讲,它必须使用至少 2 个参数调用;许多主要的预处理器(包括 MS)都接受少一个。
对于 MS 的预处理器,它使用一个参数调用:a, b, c, d, e,它与参数 first 相关联。可变参数... 为空。
对于大多数其他主要预处理器,这是用五个参数调用的。 first 与 a 相关联; ... 和 b, c, d, e。
2c.b.参数替换
对于 MS 的预处理器,替换列表中没有提到 first,因此没有对相关参数进行任何处理。 __VA_ARGS__(未被字符串化/粘贴)不符合 MS 的逗号省略功能,因此空的可变参数会导致 __VA_ARGS__ 被替换为地标。
对于大多数其他主要预处理器,替换列表中仍然没有提到first,因此a 没有任何反应。提到了__VA_ARGS__(没有被字符串化/粘贴),所以b, c, d, e被评估,导致b, c, d, e;该结果将替换 __VA_ARGS__。
2c.c.重新扫描并进一步更换
对于 MS 的预处理器,这里没有什么有趣的。完成后,地标被移除。
对于其他预处理器,b, c, d, e 再次被重新扫描,结果相同。
在使用 MSVC 时如何实现这个 EXCLUDE_FIRST_ARG 功能?
在这种情况下,您可以使用帮助程序将参数列表与宏分开:
#define _EXCLUDE_FIRST_ARG_(first, ...) __VA_ARGS__
#define CALL(A,B) A B
#define EXCLUDE_FIRST_ARG(...) CALL(_EXCLUDE_FIRST_ARG_,(__VA_ARGS__))
EXCLUDE_FIRST_ARG(a, b, c, d, e)
大致上,EXCLUDE_FIRST_ARG 在 a.s. 中扩展为 CALL(_EXCLUDE_FIRST_ARG_,(a, b, c, d, e))。采用这种方法的阶段。在调用 CALL 的重新扫描期间,在 a.s.在 each 被完全评估后,扩展为 _EXCLUDE_FIRST_ARG_ 和 (a, b, c, d, e)。然后在CALL 的重新扫描期间,参数列表再次看起来像多个令牌。