首页 > 解决方案 > 将预处理器配置定义迁移到没有内存膨胀的 constexpr

问题描述

迟到总比没有好:我正在尝试将我的代码库更新为最新的 C++ 标准,现在正试图真正理解 constexpr 语法功能的意图。

但是,我正在为此苦苦挣扎:在我的一个微控制器项目中(没有一些高级 std 功能的纯 C++),我有一个配置头,其中包含相当大的#define 语句列表,允许非开发人员用户配置某些常量值(大部分时间设备相关参数)。例子:

#define SERIAL_DEVICE_XY_BAUD_RATE (9600)
#define MAX_SENSORDATA_BUFFER_LENGTH (250)

在我看来,将其迁移到 constexpr 定义是一个很好的选择(对我来说最大的一点是命名空间和这些值的类型安全)。

现在我的问题是:虽然我想将单独的头文件与配置常量一起保存,但如何在不重复内存分配的情况下使用 constexpr?据我了解,迁移到 constexpr 如下:

constexpr unsigned int device1_baud_rate = 115200;

将给出一个完全隔离的(新)变量,并分配了适当的内存。当在程序的其他地方的类的构造函数中使用它(就像现在使用宏定义一样)时,它会被复制到成员变量中,给我留下两个变量分配而不是一个。

我想我错过了一种直接使用这种 constexpr 值的概念,有人能指出我这个用例的正确方向吗?谢谢!

标签: c++c++14constexpr

解决方案


您始终可以使用 Godbolt 之类的工具来查看编译器创建的内容。如果您检查此设置的结果,您将看到编译器将为#defineand生成相同的输出costexpr,如果您仅将它们用作常量,即使没有打开优化:

#include <iostream>

#define DEFINE_CONST (9600)
constexpr unsigned int constexpr_const = 9600;


int main() {
    int x = DEFINE_CONST;
    int y = constexpr_const;
    std::cout << constexpr_const << std::endl;
    std::cout << DEFINE_CONST << std::endl;
}

结果是:

main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 9600   // int x = DEFINE_CONST;
        mov     DWORD PTR [rbp-8], 9600   // int y = constexpr_const;

        // std::cout << constexpr_const << std::endl;
        mov     esi, 9600 
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
        mov     esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))

        // std::cout << DEFINE_CONST << std::endl;
        mov     esi, 9600
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
        mov     eax, 0


        leave
        ret

推荐阅读