【问题标题】:List initialization of const and array structure fields列出 const 和数组结构字段的初始化
【发布时间】:2020-02-15 12:29:16
【问题描述】:

最近刚开始为微控制器进行 C++ 编程,我遇到了一些情况*,在结构上使用非静态 const 字段会很方便,该结构始终保证具有固定值(对于每个sturct 的实例,永远)。

给定一个结构

struct S {
    const uint8_t c; // Should always be 42
    char v;
    uint32_t arr[4];
}

我希望c 成为一个常量值,并且每次都使用相同的常量值。我希望能够使用大括号初始化列表的便利性来设置 v 和 arr 的成员,如

S some_var = {'v', { 0, 1, 2, 3 } };

因为我希望c 是一个常量,所以我的印象是我必须使用初始化列表来设置c,例如S() : c(42) {},它工作得很好,只要我不尝试也初始化arr,在这种情况下,我不知道列表应该是什么样子。这使用 C++11 可行吗? (如果这在 C++11 中不可行,但在一些较新的标准中,也对答案感兴趣。)

示例代码:

#include <stdio.h>
#include <stdint.h>

struct S {
    const uint8_t c; // Should always be 42 on every instance
                     // of the struct due to hardware shenanigance
                     // (i.e. this struct is the representation of a register value)
    char v;
    uint32_t arr[4];

    // This allows using "S s1;"
    S() : c(42), v('a'), arr{} {}

    // This allows using "S s2 = { 'v', 0, 1, 2, 3 };" works but it's clumsy:
    S(uint32_t v, uint32_t arr0, uint32_t arr1, uint32_t arr2, uint32_t arr3) :
        c(42), v(v), arr{ arr0, arr1, arr2, arr3 } {}

    // I would like to do something along the lines of "S s2 = { 'v', { 0, 1, 2, 3 } };":
    // S(uint32_t v, uint32_t arr[4] /*?*/) :
    //     c(42), v(v), arr{/*?*/} {}

};

// Main just for the sake of completeness
int main() {
    // Works just fine
    S s1;
    printf("s1.c = %u\n", s1.c); // 42
    printf("s1.v = '%c'\n", s1.v); // a
    printf("s1.arr[3] = %u\n", s1.arr[3]); // 0

    // Initialiation like this works with the line:12 signature:
    S s2 = { 'v', 0, 1, 2, 3 };

    // I'd like to initialize like this:
    // S s2 = { 'v', { 0, 1, 2, 3 } };

    printf("s2.c = %u\n", s2.c); // 42
    printf("s2.v = '%c'\n", s2.v); // v
    printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
    return 0;
}

*关于我为什么要这样做的上下文:这似乎是一件奇怪的事情,因为如果值始终相同,为什么还要麻烦存储它呢?想象一下,所讨论的结构是一个位域,它对应于与微控制器通信的 IC 的寄存器。这些寄存器有时具有“保留”字段,数据表指定您必须在这些字段中写入的值。从程序员的角度来看,如果我不必手动设置这些位,那会很方便。

【问题讨论】:

  • 如果所有实例都具有相同的值,听起来你需要一个静态变量。这不仅会节省空间,因为所有对象都将共享一个变量,它还允许您保持聚合初始化。
  • @NathanOliver 请阅读问题末尾的脚注。假设您想将位域传递给1111000xxxxxxxxx 形式的某些硬件,其中11110000 始终相同/必需,xxxxxxxx 是您要通信的数据。
  • 注意:编译器可能会在varr 之间插入填充,这可能会扰乱与微控制器的通信。大多数编译器允许您避免填充(通过 #pragma pack 之类的东西),但这并不是真正可移植的。
  • 是的,@MaxLanghof 是正确的,我需要该值实际存在于内存中。据我了解,静态 const 不能满足这一点。关于如何改写问题以便更清楚的任何提示?我知道填充问题,我只是想保持示例简单。

标签: c++ c++11 gcc


【解决方案1】:

C++11 为您提供std::array,它就像一个原始数组,但没有任何“负数”(数组衰减,无法复制)。使用它你可以得到你想要的东西

struct S {
    const uint8_t c = 42;
    char v = 'a';
    std::array<uint32_t, 4> arr{};

    // This allows using "S s1;"
    S() {}

    S(uint32_t v, std::array<uint32_t, 4> arr) : v(v), arr{arr} {}
};

// Main just for the sake of completeness
int main() {
    // Works just fine
    S s1;
    printf("s1.c = %u\n", s1.c); // 42
    printf("s1.v = '%c'\n", s1.v); // a
    printf("s1.arr[3] = %u\n", s1.arr[3]); // 0

    S s2 = { 'v', { 0, 1, 2, 3 } };

    printf("s2.c = %u\n", s2.c); // 42
    printf("s2.v = '%c'\n", s2.v); // v
    printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
    return 0;
}

哪个输出

s1.c = 42
s1.v = 'a'
s1.arr[3] = 0
s2.c = 42
s2.v = 'v'
s2.arr[3] = 3

如果您绝对必须在S 中有一个原始数组,那么您的另一个选择是在构造函数中使用std::initializer_list。看起来像

struct S {
    const uint8_t c = 42;
    char v = 'a';
    uint32_t arr[4]{};

    // This allows using "S s1;"
    S() {}

    S(uint32_t v, std::initializer_list<uint32_t> data) : v(v)
    {
        int i = 0;
        for (auto e : data)
            arr[i++] = e;
    }
};

// Main just for the sake of completeness
int main() {
    // Works just fine
    S s1;
    printf("s1.c = %u\n", s1.c); // 42
    printf("s1.v = '%c'\n", s1.v); // a
    printf("s1.arr[3] = %u\n", s1.arr[3]); // 0

    S s2 = { 'v', { 0, 1, 2, 3 } };

    printf("s2.c = %u\n", s2.c); // 42
    printf("s2.v = '%c'\n", s2.v); // v
    printf("s2.arr[3] = %u\n", s2.arr[3]); // 3
    return 0;
}

您得到的结果与使用 std::array 的代码相同。

【讨论】:

  • 您很少看到像这样进行统一初始化:D
  • @MaxLanghof 我会说它对我来说大约有 75% 的时间有效。真正的主要问题是遗留容器。当你从头开始时,你可以为你的构造函数构建一个更好的重载集。
  • 在我的脑海中,我一直在寻找 std::initializer_list 解决方案,但 std::array 似乎很明显,我什至都没有尝试它而感到难过。
猜你喜欢
  • 1970-01-01
  • 2014-08-08
  • 2018-04-23
  • 1970-01-01
  • 2013-01-17
  • 1970-01-01
  • 2016-02-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多