Tom Dorone 54 c++ macros constexpr c++11
我应该在哪里使用宏,哪里更喜欢constexpr?它们基本不一样吗?
#define MAX_HEIGHT 720
VS
constexpr unsigned int max_height = 720;
Jonathan Wak.. 108
它们基本不一样吗?
不,绝对不是.差远了.
除了你的宏是一个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>;
Adrian Maire.. 7
一般而言,应尽可能使用constexpr
宏,只有在没有其他解决方案时才使用宏。
宏是代码中的简单替换,因此,它们通常会产生冲突(例如,windows.h max
macro vs std::max
)。此外,可以以不同的方式轻松使用有效的宏,然后触发奇怪的编译错误。(例如,Q_PROPERTY
用于结构构件)
由于所有这些不确定因素,避免使用宏是一种很好的代码风格,就像您通常避免使用goto一样。
constexpr
是在语义上定义的,因此通常产生的问题要少得多。