什么时候应该在 C++ 中使用文字类?

Posted

技术标签:

【中文标题】什么时候应该在 C++ 中使用文字类?【英文标题】:When should literal classes be used in C++? 【发布时间】:2013-01-13 20:48:27 【问题描述】:

谁能告诉我C++什么时候需要文字类? 我对 constexpr 构造函数、constexpr 成员感到有些困惑,我看不出重点是什么。我想看看它的一些实际用途。

我也想知道set成员函数是否需要constexpr,即:

constexpr void set_num(int a)  num = a; 

【问题讨论】:

你不能修改编译时对象。 您的set_num 成员似乎正试图修改某个类的成员。如果set_num 被声明为 constexpr,则无法修改对象,因此您的赋值无效。 我还没有遇到过使用 constexpr 类实例的特殊情况,但无论如何我并没有太多使用 constexpr。原则上,当您想将几个相关的 constexpr 值和函数组合在一起时,您应该创建一个类。 @VaughnCato,你说得对,constexpr 成员函数不能修改对象,但这就像任何其他 const 成员函数一样,不是因为对象是“编译时目的”。您可以拥有文字类型的可变对象。 对,就像我在下面的回答中所说:这并不意味着该类型的所有对象必须constexpr 常量,它只是意味着该类型的对象可以声明为constexpr常量 【参考方案1】:

在 C++03 中,该对象具有动态初始化

struct Data 
  int i;
  int j;
;
Data init_data();  // calculate something
const Data data = init_data();

即程序启动时,在main运行之前,会调用函数并初始化对象。

在 C++11 中,对象可以有常量初始化,一种静态初始化的形式,意味着它的值是在编译时设置的,并且在程序开始。这对于避免 static initialization order fiasco 等非常有用。为确保类型得到常量初始化,它必须由常量表达式初始化,因此必须有一个constexpr 构造函数,并且在完整表达式中调用的任何函数都必须是constexpr 函数。

Data 类型是微不足道的,所以它隐式声明的构造函数是 constexpr 构造函数,所以要使全局 data 经历常量初始化,我们只需要将 init_data() 设为constexpr函数:

struct Data 
  int i;
  int j;
;
constexpr Data init_data();  // calculate something
constexpr Data data = init_data();

文字类型的优点是此类类型可用于其他常量表达式,即需要编译时常量的上下文中。所以现在我们将data 对象作为编译时常量,我们可以在其他常量表达式中使用它,例如初始化其他编译时常量:

const int i = ::data.i;

我们可以将Data 类型用于具有类内初始化程序的静态数据成员:

struct MoreData 
  static constexpr Data zerozero = Data;  // OK, Data is a literal type
;

如果Data 不是文字类型,我们将不得不写:

struct MoreData 
  static const Data zerozero;
;

// in moredata.cc
const Data MoreData::zerozero = Data;

然后只看到标头的代码不知道MoreData::zerozero 的值,并且不能在编译时优化中使用它。

所以“文字类型”规则的优点是它们允许您定义可以在常量表达式中使用的新类类型。在 C++03 中,只有极少数类型(例如整数)可用于常量表达式,例如整数文字,例如 10x23 或整数类型的编译时常量。在 C++11 中,您可以编写自己的类型,这些类型在其构造函数中可以具有中等复杂的逻辑(可以在 constexpr 函数中表达的任何内容),但仍可用作编译时常量。

另外我想知道一个集合成员函数是否需要是 constexpr,即

constexpr 成员函数是const 成员函数的特例,因此它不能修改该类型的(非-mutable)成员。修改对象的setter函数不能是const。

要成为文字类型,类必须遵循一些规则,包括至少有一个 constexpr 构造函数。这并不意味着该类型的所有对象必须constexpr常量,它只是意味着该类型的对象可以constexpr常量,如果它们被声明为这样并使用类的constexpr 构造函数之一进行初始化。再次使用Data 示例,程序中的大多数对象都不是常量:

Data d =  0, 1 ;
d.i = d.i + 5;

因此,如果您添加了一个 setter,一个修改对象的函数,那么只有在该类型的非常量对象上使用它才有意义,并且像任何其他修改类型的函数一样,它不应该是

【讨论】:

【参考方案2】:

constexpr 修复了 C++98 中使用数值限制时的问题。在 C++11 之前的表达式,例如

std::numeric_limits<short>::max()

不能用作整数常数,尽管它几乎等于宏INT_MAX。在 C++11 中,这样的表达式被声明为 constexpr,例如, 您可以使用它来声明数组编译时计算(元编程)

std::array<float,std::numeric_limits<short>::max()> a;

【讨论】:

虽然constexpr 在日常使用中确实对这种常量返回函数最有意义,但这当然只是关于constexpr 及其用法和含义的一半真相(并且只是一半的答案实际问题)。【参考方案3】:

constexpr 类的一大优势是它们可以放入 .ro 数据中,这可以减少可执行文件大小并提高性能。特别是。对于几何类型,例如或类似的“简单”类型,这非常简洁,因为您也可以摆脱“神奇”数字。参见例如https://www.kdab.com/kdab-contributions-to-qt-5-0-part-4/.

【讨论】:

以上是关于什么时候应该在 C++ 中使用文字类?的主要内容,如果未能解决你的问题,请参考以下文章

模拟射击文字类游戏(Python)

bootstrap 排版 图片+文字类文章 常用样式

我啥时候应该在 C++ 中使用 typedef?

C++ 标准库 - 我应该啥时候使用它,啥时候不应该使用它?

什么时候适合在 C++ 中使用静态(通过未命名的命名空间)?

在啥情况下我应该在 C++ 中使用 memcpy 而不是标准运算符?