08 April 2023

C++ 核心指南目录

“Don’t use macros for program text manipulation”

理由

宏是大部分 bug 的根源。宏不遵循作用域范围规则和类型规则。宏使得人和编译器看到的东西不一样。宏使得编译过程更复杂。

坏例子

#define Case break; case   /* BAD */

这个宏看起来无伤大雅,它把小写字母的 case 变成大写字母的 Case ,但是却引入了控制过程 bug 。

注意

这条规则并不禁止使用#ifdef这类配置控制用的宏。

未来,模块导入方式可能会替代这类配置控制宏。

注意

这条规则也不鼓励使用#进行字符串化处理,不鼓励用##进行字符串拼接。这类用法跟宏一样,有时候看起来人畜无害,却也给工具会引入问题。比如自动补全、静态分析、调试器等。经常的,对这种特殊宏的需要,预示着某总过度复杂的设计。### 促进宏的定义与使用。

#define CAT(a, b) a ## b
#define STRINGIFY(a) #a

void f(int x, int y)
{
    string CAT(x, y) = "asdf";   // BAD: hard for tools to handle (and ugly)
    string sx2 = STRINGIFY(x);
    // ...
    cout << xy << endl;
    cout << sx2 << endl;
}
int main()
{
    f(10, 111);
    return EXIT_SUCCESS;
}
asdf
x

对于这种底层字符串操作的宏,有规避方法,比如:

string s = "asdf" "lkjh";   // ordinary string literal concatenation

enum E { a, b };

template<int x>
constexpr const char* stringify()
{
    switch (x) {
    case a: return "a";
    case b: return "b";
    }
}

int main()
{
    cout << s << endl;
    string sx1 = stringify<a>();
    cout << sx1 << endl;
    string sx2 = stringify<b>();
    cout << sx2 << endl;
    return EXIT_SUCCESS;
}
asdflkjh
a
b

虽然这段代码不像宏那样容易定义,但是使用起来没负担,也没有额外运行时开销。且遵循范围和类型规则。

未来,静态反射很可能免除掉字符串的预处理操作。

强化

当你看到用于代码控制之外的目的时,请尖叫。