Инициализировать массив некопируемых неподвижных объектов в другом конструкторе объектов

Этот вопрос относится как к инициализации массива, так и к иерархии модулей 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

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

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

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


person sheridp    schedule 01.05.2018    source источник
comment
Итак, вы говорите, что элемент std::array не работает, а обычный локальный массив работает. Тогда возникает очевидный вопрос: пробовали ли вы простой массив элементов?   -  person HolyBlackCat    schedule 02.05.2018
comment
Вы бы предпочли изменить имена конструкторов с CPU на Object.   -  person NuPagadi    schedule 02.05.2018
comment
Вы проверяли реализацию конструктора или даже функцию Print? Может быть, например, есть какая-то статическая переменная, которая хранит какое-то состояние, и это приводит к такому странному поведению.   -  person NuPagadi    schedule 02.05.2018
comment
@NuPagadi Обновлены имена конструкторов (моя ошибка). В конструкторе Object первый параметр правильный (имя sc_module). Я все больше и больше думаю, что это странность SystemC в том, как он определяет родителя модуля.   -  person sheridp    schedule 02.05.2018
comment
Попробуйте использовать string{"string1"} внутри фигурных скобок. Как 2_   -  person NuPagadi    schedule 02.05.2018


Ответы (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<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 «по значению», что означает, что объект, который получает конструктор, будет копией того, что вы ему передаете, если это имеет значение.) Я бы предложил задать отдельный вопрос с более подробной информацией об этой проблеме. .

person aschepler    schedule 01.05.2018
comment
Спасибо за объяснение. Это имеет смысл. Что касается вашего последнего предложения obj_array { Object{"string1", config}, Object{"string2", config} }, это не работает с: use of deleted function 'Object::Object(Object&&)', потому что оно неподвижно. Но я думаю, что вы правы, иерархия объектов, вероятно, является чем-то странным, связанным с тем, как SystemC определяет родительский модуль. - person sheridp; 02.05.2018