【问题标题】:Initialize array of non-copyable non-movable objects in another objects constructor在另一个对象构造函数中初始化不可复制的不可移动对象数组
【发布时间】:2018-05-01 21:43:42
【问题描述】:

这个问题与数组初始化和 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 <--??--> 
      {}
};

我需要用Objects初始化obj_array,不能复制也不能移动。

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

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

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

我知道很多人都问过非常相似的问题,但我想弄清楚这里到底发生了什么。在我看来,初始化器列表中的花括号太多了,但否则无法编译。

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

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

一切正常。 arr 对象名称正确,其父对象名称也正确。

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

【问题讨论】:

  • 所以你说成员std::array 不起作用,而普通的本地数组确实起作用。那么有一个明显的问题:你尝试过普通的成员数组吗?
  • 您宁愿将构造函数名称从 CPU 更改为 Object
  • 你检查了构造函数的实现,甚至是打印函数吗?可能有例如一些静态变量,它持有一些状态,它会导致这种奇怪的行为。
  • @NuPagadi 更新了构造函数名称(我的错误)。在 Object 的构造函数中,第一个参数是正确的(sc_module 的名称)。我越来越认为这是 SystemC 在如何确定模块的父级方面的奇怪之处。
  • 尝试在大括号内使用string{"string1"}。喜欢obj_array {{ {string{"string1"}, config}, {string{"string2"}, config} }}

标签: c++ initializer-list systemc


【解决方案1】:

带有双括号的表单是初始化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 };

和上面的一样。

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

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

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

编译器会这样处理它:

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

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

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

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

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

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

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

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

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

您的 SystemC 问题可能与这一切无关,因为双大括号样式是正确的,并且会达到您的预期。它可能是Configuration 类的细节。 (请注意,您的构造函数采用“按值”的Configuration 参数,这意味着构造函数获取的对象将是您传递给它的任何内容的副本,如果这很重要的话。)我建议提出一个单独的问题,详细说明关于那个问题。

【讨论】:

  • 感谢您的解释。那部分是有道理的。至于你的后一个建议obj_array { Object{"string1", config}, Object{"string2", config} } - 这失败了:use of deleted function 'Object::Object(Object&amp;&amp;)' 因为它不可移动。但我认为你是对的,对象层次结构可能与 SystemC 如何确定父模块有关。
猜你喜欢
  • 1970-01-01
  • 2012-08-14
  • 1970-01-01
  • 2016-01-08
  • 2017-07-20
  • 1970-01-01
  • 2015-12-26
  • 2021-09-10
  • 1970-01-01
相关资源
最近更新 更多