下面是重现问题的最小代码:
#include <chrono>
#include <functional>
#include <iostream>
using namespace std;
class Test {
public:
Test() {}
void test1() { cout << __func__ << endl; }
void test2() { cout << __func__ << endl; }
void testPrepare() { cout << __func__ << endl; }
private:
};
#define DO_TEST(obj, testName)
{
obj.testPrepare();
std::function<void(void)> test = std::bind(&Test::##testName, obj);
/*test();
Some other code which use test()*/
}
int main(int argc, char const *argv[]) {
Test obj;
DO_TEST(obj, test1);
DO_TEST(obj, test2);
/* code */
return 0;
}
这工作得很好,并且与cl.exe一样,但是在g++/clang++上抛出编译时错误,如下所示:
g++ d.cpp
d.cpp:19:53: error: pasting "::" and "test1" does not give a valid preprocessing token
19 | std::function<void(void)> test = std::bind(&Test::##testName, obj);
| ^~
d.cpp:26:3: note: in expansion of macro ‘DO_TEST’
26 | DO_TEST(obj, test1);
| ^~~~~~~
d.cpp:19:53: error: pasting "::" and "test2" does not give a valid preprocessing token
19 | std::function<void(void)> test = std::bind(&Test::##testName, obj);
| ^~
d.cpp:27:3: note: in expansion of macro ‘DO_TEST’
27 | DO_TEST(obj, test2);
| ^~~~~~~
编译器的细节:
- cl.exeMicrosoft (R) C/c++优化编译器版本19.29.30133用于x86
- 叮当声+ +版本10.0.0-4ubuntu1,目标:x86_64-pc-linux-gnu
- g++(Ubuntu 9.3.0-17ubuntu1~20.04)
注意:我知道将&Test::##testName
更改为&Test:: testName
将解决问题。但是我想知道是cl.exe允许上面的代码还是g++/clang++抛出错误是一个bug。
根据C规范:
每个##预处理令牌的实例在替换列表中删除并进行上述预处理令牌与以下预处理令牌. ...连接如果结果不是有效的预处理令牌,则行为未定义。
因此,以不创建单个令牌的方式使用##
是未定义的——编译器可以给出关于它的诊断,但不是必需的,并且它可以(作为扩展)做其他事情。