Элемент без конструктора по умолчанию в пользовательском контейнере шаблона - PullRequest
2 голосов
/ 22 марта 2020

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

template <typename Type>
class Container {
public:
    Container() : arraySize(10) { valueWrappers = new Type[arraySize];}

    Container(const Container& other) { /* --- */}
    ~Container() { /* --- */}
    Container& operator=(const Container& other) { /* --- */}

    /* some functions */

private:    
    int arraySize;
    Type* valueWrappers;
};

Теперь у меня проблема - когда я пытаюсь создать свой контейнер, используя в качестве шаблона класс без конструктора по умолчанию, появляется ошибка компиляции:

class MyClass {
public:
    MyClass(int value) :v(value) { }

private:
    int v;
};

int main() {
    Container<MyClass> cont;

    return 0;
}

C2512 'MyClass': не доступен соответствующий конструктор по умолчанию

Проблема в том, что мне нужно что-то инициализировать массивом значений "Type", но я не знаю, что мне нужно использовать , Я не могу использовать NULL, потому что в этом случае контейнер будет работать только с указателями. Итак, кто-нибудь может дать совет, как я могу это сделать? Или, может быть, есть другой способ решить эту задачу?

Ответы [ 3 ]

2 голосов
/ 22 марта 2020

Исходя из ваших требований, я думаю, вам придется использовать размещение new. Поскольку вы не предоставили весь соответствующий код, я собираюсь сделать то, что могу.

Во-первых, вам придется выделять необработанную память вместо непосредственного использования new.

Container() : arraySize(10) { valueWrappers = reinterpret_cast<Type*>(::operator new(sizeof(Type) * arraySize)); }

Теперь, когда вы положили что-то в ваш Container, вам придется сконструировать его на месте, используя что-то вроде следующего:

new (valueWrappers + index) Type(arguments to type);

В вашем деструкторе вам понадобится чтобы явно вызывать деструкторы для любого объекта, для которого вы использовали размещение new.

valueWrappers[index]->~Type();

Наконец, освободите память, используя ::operator delete.

::operator delete(valueWrappers);

Пожалуйста, имейте в виду, что это очень быстрый и грязный ответ, и этот код может быть трудно отладить и поддерживать. Вам нужно будет отслеживать, какие индексы в valueWrapper были инициализированы, а какие нет во время очистки. Если возможно, я очень рекомендую использовать что-то похожее на std::vector, которое справится со всей этой сложностью для вас.

1 голос
/ 22 марта 2020

Вы можете использовать std::optional для отсрочки инициализации, которая гарантирует правильную обработку времени жизни объекта. Позволить контейнеру по умолчанию иметь 10 элементов - также сомнительный выбор - может быть предпочтителен конструктор (count).

template <typename Type>
class Container {
    using elem_t = std::optional<Type>;

    std::size_t count{};
    std::unique_ptr<elem_t[]> elems{};
public:
    Container() = default;
    Container(std::size_t cnt)
        : count{cnt}
        , elems{std::make_unique<elem_t[]>(cnt)}
    {
    }
    // for example
    template <typename... Args>
    void construct_at(std::size_t pos, Args&&... args)
    {
        assert(pos < count);
        assert(!elems[pos]);

        elems[pos].emplace(std::forward<Args>(args)...);
    }
    // ...
};

Обратите внимание, что я использовал std::unique_ptr для упрощения управления памятью; указатель также будет в порядке, хотя, по-видимому, более подвержен ошибкам. Теперь вы можете пройти контейнер и построить элементы:

class MyClass {
public:
    MyClass(int value) :v(value) { }
private:
    int v;
};

int main()
{
    Container<MyClass> cont(10);
    for (std::size_t i = 0; i < 10; ++i) {
        cont.construct_at(i, /* argument */);
    }
}
1 голос
/ 22 марта 2020

Один из вариантов - не размещать массив в конструкторе по умолчанию, а вместо этого инициализировать valueWrappers нулем. Другой вариант - не использовать конструктор по умолчанию в шаблоне. Третий вариант - сохранить ваш класс как есть и просто задокументировать, что шаблон является конструируемым по умолчанию, только если аргумент типа является конструируемым по умолчанию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...