什么时候应该在 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 中,只有极少数类型(例如整数)可用于常量表达式,例如整数文字,例如 1
或 0x23
或整数类型的编译时常量。在 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++ 中使用文字类?的主要内容,如果未能解决你的问题,请参考以下文章
C++ 标准库 - 我应该啥时候使用它,啥时候不应该使用它?