2015-08-27 35 views
5

看起来,如果将具有多个参数的模板实例化作为参数传递给宏,则C++预处理器将失败。C++预处理器不知道模板参数?

查看下面的示例。

#include <stdio.h> 

#define FOO(v) printf("%d\n",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("%d\n",bar<1>::val()); 
    printf("%d\n",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 
} 

Tested铿锵++和g ++

它应该被看作是一个错误吗?

+1

你可以设计宏提取括号参数里面,但这个例子当然不需要宏。 – chris

+0

一个相关的问题:C++预处理器是否知道C++? AFAICT的C++预处理器并没有太大的改变,因为它只是C预处理器...;) –

+1

@chris,谢谢,从您的消息的措辞我找到了类似问题的答案http://stackoverflow.com/questions/13842468/comma-in-cc-macro/13842784#13842784 – hutorny

回答

2

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("%d\n",__VA_ARGS__::val()) 
    
11

不,这不是一个错误。

c预处理器是与其他语言不同的野兽,它按照自己的规则进行播放。改变这种做法会大大地破坏兼容性,CPP是高度标准化的。

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

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

宏的说法被视为纯文本字符串和参数使用逗号分隔。因此,模板中的逗号将被视为分隔符。因此,预处理器会认为你已经将两个参数传递给一个参数宏,因此出现错误。