首页 > 解决方案 > 在另一个对象构造函数中初始化不可复制的不可移动对象数组

问题描述

这个问题与数组初始化和 SystemC 模块层次结构有关。

我有一个不可复制、不可移动且没有默认构造函数的类:

class Object : public sc_module {
    public:
      Object(string name, Configuration config);
      Object(const CPU& that) = delete;

};

和另一个具有这些数组的类:

class Object2 : public sc_module {
    public:
      std::array<Object, 2> obj_array;
      Object2() : obj_array <--??--> 
      {}
};

我需要用 初始化obj_arrayObjects不能复制也不能移动。

我尝试了一大堆组合,编译的一件事是:

class Object2:  {
    public:
      std::array<Object, 2> obj_array;
      //note the double braces on this initializer list
      Object2() : obj_array {{ {"string1", config}, {"string2", config} }} 
      {}
};

它可以工作,但我稍后在代码中看到了一些奇怪的行为:当我打印出 in 的名称时Objectsobj_array第一个是正确Object2.string1的,但下一个真的很奇怪Object2.string1.string2,当它应该是Object2.string2)。

我知道很多人都问过非常相似的问题,但我正试图弄清楚这里到底发生了什么。在我看来,我的初始化列表中的大括号太多了,但否则它不会编译。

我正在使用带有标志 -std=c++14 的 g++ 6.4.0

问题是如果我在构造函数的主体中创建另一个数组,例如 class Object2: { public: std::array obj_array; //注意这个初始化列表中的双括号 Object2() : obj_array {{ {"string1", config}, {"string2", config} }} { Obj arr[2] {{"test", config}, { “test2”,配置}};} };

一切看起来都很好。 arr对象名称是正确的,它们的父项也是正确的。

我知道这是一个深奥的问题,但我需要帮助了解在我的 obj_array 初始化期间到底发生了什么机制。

标签: c++initializer-listsystemc

解决方案


带有双括号的表单是初始化 a 的“完整”方式std::array,因为std::array它实际上是一个包含原始 C 样式数组的聚合结构。本质上是

namespace std {
    template <class T, size_t N>
    struct array {
        T __some_internal_name[N];

        // public members...
    };
}

所以像这样的初始化:

std::array<int, 3> A{{ 2, 3, 4 }};

是有效的,因为外部{}用于std::array结构对象,内部{}用于它包含的原始 C 样式数组。

通常,您不需要双括号,因为有一条规则允许您在另一个聚合中省略一些嵌套括号。这就是为什么这样的初始化:

std::array<int, 3> A{ 2, 3, 4 };

与上述相同。

规则是,当编译器将初始化列表的片段关联到聚合的子对象(结构/类的非静态数据成员或数组的元素)时,如果下一个子对象也是聚合(至少包含它自己的一个子对象)并且初始化器列表的下一部分不以{令牌开头,则子对象从初始化器列表中获取与它自己的直接子对象一样多的部分。但是,如果初始化列表中的下一个内容以 a 开头{,则假定它是该聚合子对象的完整初始化列表。

所以如果你有几乎正确的代码

Object2() : obj_array { {"string1", config}, {"string2", config} }
   {}

编译器处理它是这样的:

  1. obj_array是 a std::array<Object, 2>,它是一个聚合结构,它的初始化器是一个花括号初始化器列表,因此应用了聚合初始化。

  2. 第一个{obj_array对象相关联。

  3. obj_array有一个类型为 的子对象Object[2]。这种类型也是一个聚合类型,initializer-list 中的下一个东西以 开头{,因此它打开了另一个 initializer-list 来初始化Object[2]数组。

  4. Object[2]包含两个子对象,都是 type ,这Object是一个非聚合类类型。

  5. 第一个Object必须用初始化列表中的下一个东西来初始化,即"string1". 但是没有从const char[8]到的有效转换Object。这是一个错误。

之后可能会出现其他错误,具体取决于编译器如何尝试继续。

简而言之,单大括号样式std::array在您的情况下不起作用的原因是,由于您打算成为Object初始化程序的内容以 a 开头{,因此它被视为 C 样式数组的初始化程序。

双括号解决方案有效,或者您也可以指定Object类型以避免使用另一个初始化列表开始初始化列表元素:

Object2() : obj_array { Object{"string1", config}, Object{"string2", config} }
   {}

您的 SystemC 问题可能与这一切无关,因为双括号样式是正确的,并且会按照您的预期进行。这可能是Configuration课程的一个细节。(请注意,您的构造函数采用Configuration“按值”参数,这意味着构造函数获取的对象将是您传递给它的任何内容的副本,如果这很重要。)我建议提出一个单独的问题,并提供有关该问题的更多详细信息。


推荐阅读