Инициализировать массив не копируемых неподвижных объектов в другом конструкторе объектов - PullRequest
0 голосов
/ 02 мая 2018

Этот вопрос относится как к инициализации массива, так и к иерархии модулей 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_array с помощью Objects, который не может быть скопирован и не может быть перемещен.

Я перепробовал целую кучу комбинаций, и единственное, что компилируется:

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

Это работает, но позже я вижу странное поведение в коде: когда я печатаю имена Objects в obj_array, первое из них правильное Object2.string1, а следующее действительно странное Object2.string1.string2 когда это должно быть просто Object2.string2).

Я знаю, что многие люди задавали довольно похожие вопросы, но я пытаюсь точно выяснить, что здесь происходит. Мне также кажется, что в этом списке инициализатора слишком много скобок, но иначе он не скомпилируется.

Использую g ++ 6.4.0 с флагом -std = c ++ 14

Дело в том, что если я создам другой массив в теле конструктора, например класс Object2: { общественности: std :: array obj_array; // обратите внимание на двойные скобки в этом списке инициализатора Object2 (): obj_array {{{"string1", config}, {"string2", config}}} { Obj arr [2] {{"test", config}, {"test2", config}}; } }; * * Тысяча двадцать-семь

Все выглядит нормально. arr Имена объектов верны, как и их родители.

Я знаю, что это эзотерический вопрос, но мне нужна помощь, чтобы понять, какие именно механизмы происходят во время моей инициализации obj_array.

1 Ответ

0 голосов
/ 02 мая 2018

Форма с двойными скобками - это «полный» способ инициализации 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 struct, а внутренние {} для необработанного массива в стиле C, который он содержит.

Но обычно , вам не нужны двойные скобки, потому что есть правило, которое позволяет вам опустить некоторые вложенные скобки для агрегата внутри другого агрегата. Вот почему инициализация вроде:

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

делает то же самое, что и выше.

Правило состоит в том, что, поскольку компилятор связывает части списка инициализатора с подобъектами (не статическими членами данных структуры / класса или элементами массива) агрегата, если следующий подобъект также является агрегатом (который содержит хотя бы один собственный подобъект), и следующий фрагмент списка инициализаторов не начинается с токена {, тогда подобъект берет столько частей из списка инициализаторов, сколько у него есть собственные собственные подчиненные объекты. Но если следующая вещь в списке инициализаторов начинается с {, предполагается, что это полный список инициализаторов для этого совокупного подобъекта.

Так что, если у вас есть почти правильный код

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

компилятор обрабатывает его примерно так:

  1. obj_array - это std::array<Object, 2>, который является агрегатной структурой, а его инициализатор представляет собой фигурный инициализатор-список, поэтому применяется агрегатная инициализация.

  2. Первый { связан с объектом obj_array.

  3. obj_array имеет один подобъект типа Object[2]. Этот тип также является агрегатным, и следующая вещь в списке инициализаторов начинается с {, поэтому он открывает другой список инициализаторов для инициализации массива 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 «по значению», что означает, что объект, который получает конструктор, будет копией того, что вы ему передадите, если это имеет значение.) Я бы предложил задать отдельный вопрос с более подробной информацией о эта проблема.

...