C++ 抱怨 __VA_ARGS__。

5
以下代码已经使用gcc-5.4.0编译,没有出现任何问题:
% gcc -W -Wall a.c
...

#include <stdio.h>
#include <stdarg.h>

static int debug_flag;
static void debug(const char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   vfprintf(stderr, fmt, ap);
   va_end(ap);
}

#define DEBUG(...)               \
   do {                 \
      if (debug_flag) {       \
         debug("DEBUG:"__VA_ARGS__);  \
      }              \
   } while(0)

int main(void)
{
   int dummy = 10;
   debug_flag = 1;
   DEBUG("debug msg dummy=%d\n", dummy);
   return 0;
}

然而,使用g++编译会产生有趣的影响:

% g++ -W -Wall -std=c++11 a.c
a.c: In function ‘int main()’:
a.c:18:10: error: unable to find string literal operator ‘operator""__VA_ARGS__’ with ‘const char [8]’, ‘long unsigned int’ arguments
    debug("DEBUG: "__VA_ARGS__); \

% g++ -W -Wall -std=c++0x
<same error>

% g++ -W -Wall -std=c++03
<no errors>

debug("DEBUG:"__VA_ARGS__);更改为debug("DEBUG:" __VA_ARGS__);,即在__VA_ARGS__之前加上空格,可以使代码在所有三个-std=选项下编译通过。

这种行为的原因是什么呢?谢谢。

1个回答

9

自从C++11以来,支持用户定义字面量,它们是字面量,包括字符串字面量,紧接着(没有空格)跟随一个标识符。用户定义字面量被视为单个预处理器标记。有关其目的的详细信息,请参见https://en.cppreference.com/w/cpp/language/user_literal

因此,"DEBUG:"__VA_ARGS__是一个单一的预处理器标记,在宏定义中没有特殊含义。正确的行为是将其不变地放置到宏扩展中,然后在那里无法编译,因为没有声明用于__VA_ARG__后缀的用户定义字面量运算符。

因此,GCC正确地将其拒绝为C++11代码。

这是C++03和C++11之间不兼容的变化之一,列在C++11标准草案N3337的附录中:https://timsong-cpp.github.io/cppwp/n3337/diff.cpp03.lex

在C++11之前,字符串字面量(直到结束符")将成为自己的预处理器令牌,后续标识符是第二个预处理器令牌,即使它们之间没有空格。

因此,GCC在C++03模式下接受它也是正确的。(-std=c++0x-std=c++11相同,当C++11仍处于草案阶段时,C++0x是占位符名称)

这也与C不兼容(迄今为止所有修订版都如此),因为C也不支持用户定义文字,并且将"DEBUG:"__VA_ARGS__的两个部分视为两个预处理器令牌。

因此,GCC将其作为C代码接受也是正确的(这就是gcc命令解释.c文件的方式,与g++将其视为C++不同)。

要解决此问题,请在"DEBUG:"__VA_ARGS__之间添加一个空格,正如您建议的那样。这应该使其与所有C和C++版本兼容。


修复方法在此答案的末尾,供扫描此内容的任何人使用:“要修复此问题,请像您建议的那样在“DEBUG:”和“__VA_ARGS__”之间添加一个空格。这应该使其与所有C和C++版本兼容。”注意:这是我用于C和C++的DEBUG_PRINTF()实现 - Gabriel Staples

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