在没有内存膨胀的情况下将预处理器配置定义迁移到 constexpr

Migrating preprocessor configuration definitions to constexpr without memory bloat

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

但是,我正在努力解决这个问题:在我的一个微控制器项目(没有一些高级标准功能的纯 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;

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

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

您始终可以使用像 godbolt 这样的工具来查看编译器创建的内容。如果您检查 this setup 的结果,您将看到编译器将为 #definecostexpr 生成相同的输出,如果您仅将它们用作常量,即使没有打开优化:

#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