这是我读到的:对静态成员函数使用别名?
在答案中,我看到了使用constexpr
的建议.将constexpr
用于void
函数的原因是什么?
请演示一个简单的用例。我是constexpr
新手,所以在复杂的例子中,我不会理解要点。
Rahul的回答引用了允许void
constexpr
函数的标准段落,但它没有给出用例。我想到的一个用例是有一个constexpr
类,并像往常一样分解出帮助程序方法中方法的常见行为。该标准明确提到了执行检查的函数,例如断言。我手头没有具体的例子,但我可以想象类似
class A
{
public:
constexpr X doSomething(Y arg1) {
checkInvariant();
constraintOnYArgument(arg1);
// ...
checkInvariant();
}
constexpr X doSomethingElse(Y arg1) {
checkInvariant();
constraintOnYArgument(arg1);
// ...
checkInvariant();
}
private:
constexpr void constraintOnYArguments(Y arg) {
}
constexpr void checkInvariant() {
// some checks
if (!some condition) {
throw std::logic_error("Oh no!");
}
}
};
根据 C++ 14 标准,void 是一种文字类型
如果类型是以下类型,则类型为文本类型:
— 无效;或
— 标量类型;或
— 引用类型;或
— 文字类型的数组;或
—具有以下所有属性的类类型(条款 9(: — 它有一个微不足道的析构函数,
— 它是一个聚合类型 (8.5.1( 或至少有一个 constexpr 不是副本或移动的构造函数或构造函数模板 构造函数,以及
— 它的所有非静态数据成员和基类都是 非易失性文本类型。
从这里:
允许使用任意表达式语句,以便允许 调用执行检查的函数并允许类似断言 构建。void 也变成了文字类型,因此 constexpr 仅用于执行此类检查的函数可能返回 void。
一个合理的用例是操作易失性变量。以下是使用GNU工具(g++,ld(进行嵌入式编程的一个非常简单的情况:
要使外围设备的地址成为constexpr
,您需要将其放在固定位置。这必须在链接器脚本中完成:
⋮
/* Define output sections */
SECTIONS
{
GPIO 0x48000000 (NOLOAD) : { *(.GPIO) }
⋮
现在,.GPIO
部分位于固定地址0x48000400
。外设可以通过包含易挥发性成员的 POD 进行建模。在以下示例中,POD 名为 gpio_t
,并且只有一个成员:mode
。可以在constexpr
函数中设置成员。当然,使用函数将变量设置为常量值没有任何好处。但在实际用例中,必须计算值和地址。例如,考虑为波特率设置一个分频器。
struct gpio_t {
volatile std::uint32_t mode;
};
__attribute__ ((section (".GPIO"))) gpio_t Gpio = {0};
static constexpr gpio_t *port {&Gpio};
static constexpr void init () {
port->mode = 42u;
};
void main {
init ();
⋮
};
注意:将整数强制转换为地址的 C 样式习惯用法不起作用,因为它reinterpret_cast<>
不符合创建constexpr
指针的条件(请参阅 自 C++14 非法(。以下操作失败:
constexpr gpio_t *port {(gpio_t *) 0x48000400};
只要参数号是整数常数,此 constexpr 版本将在编译时计算结果(仅限 C++11 个编译器(。当数字是运行时整数时,相同的函数完全能够在运行时计算结果。因此,您不需要同一程序的两个不同版本:一个用于编译时,另一个用于运行时。一个实现可以完成所有操作。