如果在语法中添加额外的一组括号,则可以使用相对较少的宏而不限制“必需”子句的数量:
template_((class A, class B, class C)
(requires IsFoo<A> && IsBar<B>)
(requires IsBaz<C>)
)
void frobozzle(A, B, C);
宏:
使用这些宏,上面的示例会扩展为:
template <class A, class B, class C,
int dummy = 0,
std::enable_if_t<dummy == 0 && (IsFoo<A> && IsBar<B>), int> = 0,
std::enable_if_t<dummy == 0 && (IsBaz<C>), int> = 0>
void frobozzle(A, B, C);
解释
考虑以下宏定义:
#define a(x) [x] b
#define b(x) [x] a
使用这些,这个:
a (1) (2) (3) (4)
会引起以下“连锁反应”的扩张:
a (1) (2) (3) (4)
[1] b (2) (3) (4)
[1] [2] a (3) (4)
[1] [2] [3] b (4)
[1] [2] [3] [4] a
递归在预处理器中不被允许,但这种链式反应不是递归,因为宏的调用仅发生在前一个宏扩展后,而不是在其期间,因为
(
不是扩展的一部分。(尽管请参见
https://wg21.link/cwg268)。不幸的是,虽然这将很好地循环遍历一系列
(A)(B)(C)
,但它会在末尾留下一个额外的标记: 两个使用的宏之一的名称。我用来摆脱它的技巧是用另一个宏调用包装整个列表,它将在完全扩展后附加(使用连接运算符
##
)
_END
,所以它将变成:
[1] [2] [3] [4] a_END
然后我们可以通过定义以下内容来简单地摆脱最后一个标记:
#define a_END
#define b_END
如果我们无法包含整个列表,就没有办法知道何时到达最后一个元素。唯一发生的事情是最后一个 a
或 b
没有后面跟随的 (
,这意味着它不会被展开,因为 a
和 b
是函数式宏。(我们不能只定义 a
和 b
为空,因为 a
和 b
已经是宏,尽管它们没有 (
时不会被展开。)
为什么需要两个宏?
当我们尝试像上面那样使用一个宏来引起连锁反应时,会出现问题,因此需要使用两个宏,如下所示:
#define a(x) [x] a
它不会起作用:
a (1) (2) (3) (4)
[1] a (2) (3) (4) // Doesn't expand further
这是由于“(无限)递归保护”机制的工作方式:在宏扩展期间,如果产生了与正在扩展的宏名称相同的令牌,则该令牌被标记为“不可扩展”,这意味着它永远不会再次扩展。详见
http://eel.is/c++draft/cpp.rescan#2
这意味着扩展的
a
被标记为“不可扩展”,我们的连锁反应在第一步后立即停止。我们通过使用两个宏来解决此规则:
a(..)
将不会产生任何带有其自己名称的标记,而只会使用另一个宏
b
的名称。
a
的扩展就在此结束,在
b 被扩展之前,因为在
a 的扩展中还没有
(。扩展完成后,我们不再“在
a 内”,令牌被重新检查,找到
b(..)的正确调用。它将再次生成具有名称
a的标记,但由于我们不再处于第一个
a 的扩展中,因此不会将其标记为“不可扩展”,连锁反应继续进行。
(...)
和__VA_ARGS__
对你有用吗?我不确定你在问什么。 - user8782808(A)(B)(C)
。其余部分我可以自己完成。 - Eric Niebler(A)(B)(C)
,还是((A)(B)(C))
也可以? - Mara Bos