宏定义和constexpr的区别

Reference:

Constexpr与宏

问题描述

使用VS编写代码时, 总是被提醒让把#define转换成constexpr, 不解其故, 所以上网查询了一下

原文摘录

它们基本不一样吗?

不,绝对不是.差远了.

除了你的宏是一个int而你的constexpr unsigned是一个unsigned,你有一个重要的区别,宏只有一个优势.

范围

宏由预处理器定义,并且每次发生时都会简单地替换为代码.预处理器很笨,不懂C++语法或语义.宏忽略名称空间,类或功能块等范围,因此您不能在源文件中使用其他任何名称.对于定义为正确的C++变量的常量,情况并非如此:

#define MAX_HEIGHT 720
constexpr int max_height = 720;

class Window {
// ...
int max_height;
};

调用成员变量是很好的,max_height因为它是一个类成员,因此具有不同的作用域,并且与命名空间作用域不同.如果您尝试重用该MAX_HEIGHT成员的名称,那么预处理器会将其更改为无法编译的无意义:

class Window {
// ...
int 720;
};

这就是为什么你必须给宏UGLY_SHOUTY_NAMES以确保它们脱颖而出,你可以小心命名它们以避免冲突.如果不必要地使用宏,则不必担心(并且不必阅读SHOUTY_NAMES).

如果你只想在一个函数内部使用一个常量,你不能用宏来做,因为预处理器不知道函数是什么或它在里面意味着什么.要将宏限制为仅限文件的某个部分,您需要#undef再次使用它:

int limit(int height) {
#define MAX_HEIGHT 720
return std::max(height, MAX_HEIGHT);
#undef MAX_HEIGHT
}

与更明智的比较:

int limit(int height) {
constexpr int max_height = 720;
return std::max(height, max_height);
}

为什么你更喜欢宏观?

一个真实的内存位置

constexpr变量*是一个变量,*因此它实际存在于程序中,您可以执行普通的C++操作,例如获取其地址并绑定对它的引用.

此代码具有未定义的行为:

#define MAX_HEIGHT 720
int limit(int height) {
const int& h = std::max(height, MAX_HEIGHT);
// ...
return h;
}

问题是它MAX_HEIGHT不是变量,因此必须由编译器创建对std::max临时的调用int.std::max然后返回的引用可能引用那个临时的,该语句在该语句结束后不存在,因此return h访问无效的内存.

这个问题根本不存在适当的变量,因为它在内存中有一个固定的位置,不会消失:

int limit(int height) {
constexpr int max_height = 720;
const int& h = std::max(height, max_height);
// ...
return h;
}

(在实践中,你可能会声明int h不是,const int& h但问题可能出现在更微妙的背景下.)

预处理器条件

唯一一次选择宏是指您需要预处理器理解其值,以便在#if条件中使用,例如

#define MAX_HEIGHT 720
#if MAX_HEIGHT < 256
using height_type = unsigned char;
#else
using height_type = unsigned int;
#endif

您无法在此处使用变量,因为预处理器无法理解如何通过名称引用变量.它只能理解基本的非常基本的东西,比如宏扩展和以#(#include#define#if)开头的指令.

如果您想要预处理器可以理解的常量*,*那么您应该使用预处理器来定义它.如果您想要普通C++代码的常量,请使用普通的C++代码.

上面的示例只是为了演示预处理器条件,但即使该代码也可以避免使用预处理器:

using height_type = std::conditional_t<max_height < 256, unsigned char, unsigned int>;