具有聚合初始化的 C++ 常量匿名实例

C++ Constant anonymous instance with aggregate initialization

基本上我想获取常量和匿名对象的指针,例如 class 的实例、用 T {x, y, z...} 初始化的数组或结构。抱歉我的文笔不好

我尝试编写的基本代码如下:

//Clunky, Im sure there is an inbuilt class that can replace this, any information would be a nice addition
template<class T> class TerminatedArray {
public:
  T* children;
  int length;

  TerminatedArray(const T* children) {
    this->children = children;
    length = 0;
    while ((unsigned long)&children[length] != 0)
      length++;
  }
  TerminatedArray() {
    length = 0;
    while ((unsigned long)&children[length] != 0)
      length++;
  }
  const T get(int i) {
    if (i < 0 || i >= length)
      return 0;
    return children[i];
  }
};

const TerminatedArray<const int> i = (const TerminatedArray<const int>){(const int[]){1,2,3,4,5,6,0}};

class Settings {
public:
  struct Option {
    const char* name;
  };
  struct Directory {
    const char* name;
    TerminatedArray<const int> const children;
  };

  const Directory* baseDir;
  const TerminatedArray<const Option>* options;

  Settings(const Directory* _baseDir, const TerminatedArray<const Option> *_options);
};


//in some init method's:
Settings s = Settings(
  &(const Settings::Directory){
    "Clock",
    (const TerminatedArray<const int>){(const int[]){1,2,0}}
  },
  &(const TerminatedArray<const Settings::Option>){(const Settings::Option[]){
    {"testFoo"},
    {"foofoo"},
    0
  }}
);

我引用的代码在最下面,s的定义。我似乎能够初始化一个常量整数数组,但是当将相同的技术应用于 classes 时,它失败了:
error: taking address of temporary [-fpermissive]

我什至不知道 C++ 是否支持这样的事情,我想避免必须有单独的 const 定义弄脏和分割代码,而是让它们干净和匿名。

希望将所有这些定义作为常量的原因是我正在从事一个 Arduino 项目,该项目需要 SRAM 与闪存的有效平衡。我有很多 Flash 可供使用。


我的问题是这样的。如何使用聚合初始化声明常量匿名 class/struct?

TerminatedArray 的直接(更好)等价于 std::initializer_list:

class Settings {
public:
  struct Option {
    const char* name;
  };
  struct Directory {
    const char* name;
    std::initializer_list<const int> const children;
  };

  const Directory* baseDir;
  const std::initializer_list<const Option>* options;

  Settings(const Directory& _baseDir, const std::initializer_list<const Option>& _options);
};


//in some init method's:
Settings s = Settings(
  {
    "Clock",
    {1,2,0}
  },
  {
    {"testFoo"},
    {"foofoo"}
  }
);

https://godbolt.org/z/8t7j0f

但是,这几乎肯定会出现生命周期问题(编译器试图通过 "taking address of temporary" 警告您)。如果您想存储一个(非拥有)指针(或引用),那么其他人应该拥有该对象的所有权。但是当用这样的临时对象初始化时,没有其他人会这样做。临时对象在完整表达式的末尾死亡,因此您存储的指针现在指向死对象。解决这个问题是另一回事(可能会使您的要求发生冲突)。

有点相关,我不确定将 std::initializer_list 存储为 class 成员是否是个好主意。但它肯定是您可以用作函数参数以使聚合初始化更好的东西。

&children[length] != 0还是真还是UB.

如果不想分配内存,可以参考现有数组:

class Settings {
public:
  struct Option {
    const char* name;
  };
  struct Directory {
    const char* name;
    std::span<const int> const children;
  };

  const Directory baseDir;
  const std::span<const Option> options;

  Settings(Directory baseDir, span<const Option> options);
};

//in some method:
const std::array<int, 3> ints{{1,2,0}};
const std::array<Settings::Option> options{{"testFoo"}, {"foofoo"}};
Settings s{"Clock", {ints}}, options};

首先,您没有聚合初始化任何东西。这是统一初始化,您调用构造函数而不是直接初始化成员。这是因为您的 类 具有用户定义的构造函数,而具有构造函数的 类 无法进行聚合初始化。

其次,你并不是真的能够到"initialize a constant array of integers"。它只是编译。尝试 运行 它会给出未定义的行为 - 在我的例子中,尝试构建 i 会无限搜索元素值 0.

在C++中,栈上有值,堆上有值,还有临时值(对于这句话,我真诚地向所有了解C++的人道歉)。

  • 堆上的值有永久地址,您可以自由传递。
  • 堆栈上的值具有临时地址,有效期至 块的末尾。
  • 临时值要么没有地址 (正如您的编译器警告您的那样)或在此期间拥有有效地址 他们所使用的表达方式。

您正在使用这样的临时文件来初始化 i,并试图存储和使用临时文件的地址。这是一个错误,要修复它,如果您不打算在数组所在的块之外使用 i,则可以在堆栈上创建 "temporary" 数组。

或者您可以在堆上创建数组,使用其地址初始化 i,并记得在完成后显式删除数组。

我建议在尝试修复此代码之前阅读 https://isocpp.org/faq 并熟悉变量的生命周期和内存管理。它应该让您更好地了解您需要做什么才能使您的代码执行您希望它执行的操作。

祝你好运。