宏函数对于每个C++程序员都决不陌生,就算是初出茅庐的C++程序员也知道如何定义、使用宏函数。
 
但是当初学者看到类似于以下这种宏函数嵌套的时候,可能还是会比较嘀咕,

#define CONVERTSTR(x) #x
#define CONVERTSTR2(x) CONVERTSTR(x)

第二个宏函数所做的事情不就是再一次调用上面的宏函数吗,这难道不属于画蛇添足吗?这样做有什么意义呢?别急,我们慢慢来捋一下。
 

了解#和##

要想熟练的写出宏函数,了解其中的操作符必不可少,在预编译体系自定义的几个操作符中, #和##比较特殊,它们的作用是:

  • 将标识符转换为字符串,它又被称为字符串化操作符,用法如下

#define CONVERTSTR(x) #x
string s3 { CONVERTSTR(4) }; //这里CONVERTSTR(4)被扩展为"4"
  • 将不同的标识符连接起来,它被称为符号连接操作符,用法如下

struct ABC
{

};

#define DECLARE_MAKE(x) x* Make_##x() {return new x();}
DECLARE_MAKE(ABC) //被扩展为 ABC* Make_ABC{return new ABC();}
ABC * ap = Make_ABC();

可见这两操作符的运算结果取决于传入的标识符的名称,那么如果传入的标识符本身就是一个宏变量呢?
 

宏变量乱入的情况

还是刚刚的例子,

#define CONVERTSTR(x) #x
#define VAR 10
std::cout << CONVERTSTR(VAR);

猜猜,这个时候的输出是多少?10 还是 VAR?
按照预处理器替换的原则,VAR被替换成10,接着10被转换为"10",但是真是这样吗?

运行之后发现,输出是VAR不是10,为什么呢?
 

替换规则

这是因为当宏函数中,如果包含了#或者##,替换规则会比较特殊,引用一段原文如下:

简而言之,对于宏函数来说,一般情况下当看到函数体的时候,参数替换就已经完成了(像用10替换VAR),但是对于有操作符#和##的参数,这个参数替换步骤就不会发生,所以CONVERTSTR(VAR)只会扩展为 "VAR"而不会扩展为"10"
 

修复方法

其实讲到这里答案已经很明显了,使用间接宏函数能完美解决这个问题

#define CONVERTSTR(x) #x
#define CONVERTSTR2(x) CONVERTSTR(x)

在原有函数的基础上再定义一个包装函数,这个包装函数并没有任何#或者##,这样就确保了参数可以正确展开,接着转发请求给真正需要使用的那个函数。

#define VAR 10
std::cout << CONVERTSTR2(VAR);

这样就能确保在使用VAR调用函数的时候它已经被正确展开了。

这就是间接宏函数和为什么要使用它们的原因,希望下次看到它们的时候不要再觉得这是画蛇添足了哟。

04-02 08:54