Constexpr与宏

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>;

  • @underscore_d是,但这不会更改参数。该变量不需要存储,除非对其进行了多次使用。关键是,当需要带存储的实型变量时,constexpr变量会做正确的事情。 (2认同)

Adrian Maire.. 7

一般而言,应尽可能使用constexpr宏,只有在没有其他解决方案时才使用宏。

理由:

宏是代码中的简单替换,因此,它们通常会产生冲突(例如,windows.h maxmacro vs std::max)。此外,可以以不同的方式轻松使用有效的宏,然后触发奇怪的编译错误。(例如,Q_PROPERTY用于结构构件)

由于所有这些不确定因素,避免使用宏是一种很好的代码风格,就像您通常避免使用goto一样。

constexpr 是在语义上定义的,因此通常产生的问题要少得多。

  • 使用#if条件编译,即预处理器实际上有用的东西。定义常量不是预处理器有用的功能之一,除非常量_must_是一个宏,因为它在使用#if的预处理器条件中使用。如果常量用于普通C ++代码(而不是预处理程序指令),则使用普通C ++变量,而不是预处理程序宏。 (2认同)