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

5
似乎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
}

已经使用clang++和g++进行测试

这是否应该被视为一个bug?


1
你可以设计宏来提取括号内的参数,但这个例子显然不需要宏。 - chris
一个相关的问题:C++预处理器是否意识到C++?据我所知,自从它只是C预处理器以来,C++预处理器几乎没有什么变化... ;) - Jeremy Friesner
1
@chris,谢谢,通过你的信息,我找到了一个类似问题的答案:https://dev59.com/9GYr5IYBdhLWcg3wOX26#13842784。 - hutorny
那是一个不错的链接,感谢你转发它。 - Chris Beck
预处理器对C++语法一无所知,它是一个非常愚蠢的宏处理器,遵循非常简单的规则。 - Jonathan Wakely
4个回答

11

不,这不是一个错误。

c 预处理器与语言的其余部分不同,并按照自己的规则运行。更改这个将会以大规模的方式破坏兼容性,C++ 高度严格地标准化了预处理器。

解决逗号问题的常规方法是,

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

3
C/C++预处理器将逗号视为宏参数分隔符,除非它们嵌套在括号内。只有括号。方括号、大括号和模板标记不计入其中:
列表中的各个参数由逗号预处理令牌分隔,但是匹配的内部括号之间的逗号预处理令牌不会分隔参数。(C++14 §16.3/11; C11 §6.10.3/11)
(上述情况的一个副作用是,您可以使用不平衡的大括号和方括号作为宏参数。这通常不是一个很好的主意,但如果必须这样做,你可以这样做。)
由此偶尔会出现问题;一个常见的问题是当参数应该是一段代码块时,会出现不需要的多个参数:
MY_FANCY_MACRO(1000, { int i=0, j=42; ... })

在这里,宏被调用时至少需要三个参数,尽管它可能是为了接受两个参数而编写的。

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

  1. Rewrite the macro as an inline function. If the argument is a code block, consider using a templated function which could accept a lambda or other functor. If the argument is a type, make it a template argument instead.

  2. If surrounding the argument with redundant parentheses is syntactically valid, do that. But in such a case it is almost certainly the case that suggestion (1) above would have worked.

  3. Define:

    #define COMMA ,
    

    and use it where necessary:

     FOO(baz<20 COMMA 30>);
    

    This doesn't require modifying the macro definition in any way, but it will fail if the macro passes the argument to another macro. (The replacement will be done before the inner macro call is parsed, so the multiple argument problem will just be deferred to the inner call.)

  4. If you expect that one macro argument might contain unprotected commas, and it is the last or only argument, and you're in a position to modify the macro, and you're using C++11/C99 or better (or gcc, which has allowed this as an extension for some time), make the macro variadic:

    #define FOO(...) printf("%d\n",__VA_ARGS__::val())
    

1
宏的参数被视为纯文本字符串,并使用逗号分隔参数。因此,模板中的逗号将被视为分隔符。因此,预处理器将认为您已将两个参数传递给单个参数宏,从而导致错误。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接