c++预处理器不知道模板参数

C++ preprocessors are not aware of template arguments?

本文关键字:参数 不知道 预处理 处理器 c++      更新时间:2023-10-16

看起来,如果将多个参数传递给宏作为参数的模板实例化,c++预处理器将失败。

请看下面的例子。

#include <stdio.h>
#define FOO(v) printf("%dn",v::val())
template<int N>
struct bar {
    static int val() { return N; }
};
template<int N, int M>
struct baz {
    static int val() { return N+M; }
};
int main() {
    printf("%dn",bar<1>::val());
    printf("%dn",baz<1,2>::val());
    FOO(bar<10>);       // OK
    FOO(baz<20,30>);    // error: too many arguments provided to function-like macro invocation
    FOO((baz<20,30>));  // error: '::val' has not been declared
}

用clang++和g++测试

它应该被认为是一个bug吗?

不,这不是bug。

c预处理器与语言的其他部分不同,它有自己的规则。改变这个会在很大程度上破坏兼容性,CPP是高度严格标准化的。

通常解决这些逗号问题的方法是,

typedef baz<20,30> baz2030_type;
FOO(baz2030_type);

C/c++预处理器将逗号识别为宏参数分隔符,除非它们嵌套在括号内。括号。括号、大括号和模板标记不算数:

列表中的单个参数由逗号预处理标记分隔,但是匹配的内括号之间的逗号预处理标记不会分隔参数。(c++ 14教派;16.3/11;C11教派;6.10.3/11)

(上面的一个副作用是可以使用不平衡的大括号和大括号作为宏参数。这通常不是一个很好的主意,但如果你有必要,你可以这样做。

结果偶尔会出现问题;一个常见的问题是当参数应该是一个代码块时,不需要多个参数:

MY_FANCY_MACRO(1000, { int i=0, j=42; ... })

在这里,宏被调用(至少)3个参数,尽管它可能被编写为接受2个参数。

使用现代c++(和C)编译器,您有几个选项。以相当主观的顺序:

  1. 将宏重写为内联函数。如果参数是一个代码块,请考虑使用可以接受lambda或其他函子的模板化函数。如果参数是类型,则将其改为模板参数。

  2. 如果用多余的圆括号括住参数在语法上是合法的,那么就这样做。但在这种情况下,几乎可以肯定,上述建议(1)是有效的。

  3. 定义:

    #define COMMA ,
    

    并在必要时使用:

     FOO(baz<20 COMMA 30>);
    

    不需要以任何方式修改宏定义,但如果宏将参数传递给另一个宏,则会失败。(替换将在解析内部宏调用之前完成,因此多参数问题将被推迟到内部调用。)

  4. 如果您期望一个宏参数可能包含未受保护的逗号,并且它是最后一个或唯一的参数,并且您可以修改宏,并且您正在使用c++ 11/C99或更好的版本(或gcc,它已经允许将其作为一段时间的扩展),请将宏设置为可变的:

    #define FOO(...) printf("%dn",__VA_ARGS__::val())
    

宏的参数被视为纯文本字符串,参数之间使用逗号分隔。因此,模板中的逗号将被视为分隔符。因此,预处理器将认为您向单个参数宏传递了两个参数,从而产生错误。

BOOST_IDENTITY_TYPE是解决方案:https://www.boost.org/doc/libs/1_73_0/libs/utility/identity_type/doc/html/index.html

您也可以将类型包装到decltype: decltype(std::pair<int, int>()) var;中,这也会添加一个额外的括号,但不幸的是,这需要额外的tor调用。