Constexpr vs macros
Constexpr vs macros
我应该在哪里更喜欢使用 macros ,我应该在哪里更喜欢 constexpr ?他们基本上不是一样吗?
#define MAX_HEIGHT 720
vs
constexpr unsigned int max_height = 720;
它们基本上不是一样吗?
否。绝对不。
甚至都没有关闭。除了您的宏是int
,而您的constexpr unsigned
是unsigned
,存在重要的区别,宏仅具有一个优势。
范围
宏由预处理器定义,每次发生时,都可以将其替换为代码。预处理器是 DUMB ,并且不了解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 variable 是一个变量,因此它实际上存在于程序中,您可以执行正常的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>;
一般而言,您应随时使用constexpr
,并且只有在没有其他解决方案的情况下才能使用宏。
理由:
宏是代码中简单的替代品,因此,它们通常会产生冲突(例如Windows.H max
宏与std::max
)。此外,可以很容易地以不同的方式使用起作用的宏,从而可以触发奇怪的汇编错误。(例如,在结构成员上使用的Q_PROPERTY
)
由于所有这些不确定性,避免宏的代码风格是很好的代码样式,就像您通常避免使用gotos一样。
constexpr
是语义上定义的,因此通常会产生较少的问题。
乔纳森·沃克利(Jonathon Wakely)的出色答案。我还建议您查看Jogojapan的答案,以了解const
和constexpr
与CC_26之间的区别,甚至考虑使用宏的用法。
宏是愚蠢的,但以良好的方式。如今,从表面上看,它们是一个建筑物AID,因为您希望代码的非常具体的部分仅在某些构建参数"定义"的情况下进行编译。通常,所有这些是取您的宏名称,或者更好,我们称其为Trigger
,并将诸如/D:Trigger
,-DTrigger
等添加到所使用的构建工具中。
虽然宏有许多不同的用途,但这是我最常看到的两个方法,这些方法不是坏/过时的实践:
- 硬件和平台特定的代码部分
- 增加的冗长构建
因此,尽管您可以在OP的情况下实现使用constexpr
或MACRO
定义INT的相同目标,但使用现代约定时,两者不太可能重叠。这是一些尚未分阶段的常见宏观使用。
#if defined VERBOSE || defined DEBUG || defined MSG_ALL
// Verbose message-handling code here
#endif
作为宏观使用的另一个示例,假设您有一些即将发布的硬件要释放,或者可能是特定的一代,这些硬件具有其他不需要的棘手的解决方法。我们将此宏定义为GEN_3_HW
。
#if defined GEN_3_HW && defined _WIN64
// Windows-only special handling for 64-bit upcoming hardware
#elif defined GEN_3_HW && defined __APPLE__
// Special handling for macs on the new hardware
#elif !defined _WIN32 && !defined __linux__ && !defined __APPLE__ && !defined __ANDROID__ && !defined __unix__ && !defined __arm__
// Greetings, Outlander! ;)
#else
// Generic handling
#endif
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 数据成员SFINAE的C++17测试:gcc vs clang
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 正在VS调试器中监视映射条目
- Confusion: decltype vs std::function
- 将IBM Rhapsody模型集成到VS 2019中
- VS Code "command":"make"与终端窗口中的命令行"make"不同
- 使用VS Code和CMake Tools运行自定义命令
- C++ Macros #ifdef
- 修改 VS Code 中的默认C++代码段
- 如何使用c++在VS 2019上运行SQL查询
- vs 2015 constexpr变量不恒定,但与2019相比还好吗
- 完美前进使用 std::forward vs RefRefCast
- 从VS 2015更新3更新到VS2015更新3 d后浮点计算行为不同的原因
- VS 2015 链接错误 无法构建依赖于 libcurl 的项目
- consteval wrapper vs. source_location
- VS Code C++:不准确的系统包括路径错误(wchar.h,boost/lambda/lambda.hpp)
- Constexpr vs macros