Как можно инициализировать структуру, если она имеет не копируемые элементы? - PullRequest
0 голосов
/ 21 января 2019

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

 In function 'int main()':
37:3: error: no matching function for call to 'ListElem::ListElem(<brace-enclosed initializer list>)'
37:3: note: candidates are:
25:2: note: ListElem::ListElem(ListElem&&)
25:2: note:   candidate expects 1 argument, 2 provided
20:5: note: constexpr ListElem::ListElem()
20:5: note:   candidate expects 0 arguments, 2 provided

Код:

// Example program
#include <iostream>
#include <memory>
#include <vector>

class MyObj {
    public:
        virtual ~MyObj(){}
};

class MyObj2 : public MyObj{
    public:
        virtual ~MyObj2(){}
};

struct ListElem {
    std::unique_ptr<MyObj> item;
    int some_counter = 0;

    ListElem() = default;

    ListElem(ListElem const& e) = delete;
    ListElem& operator=(ListElem const& e) = delete;

    ListElem(ListElem&& e) = default;
    ListElem& operator=(ListElem&& e) = default;

};

int main()
{
  std::vector<ListElem> elems;

  ListElem item{
      std::unique_ptr<MyObj>(new MyObj2()),
      10
  };
  elems.push_back(std::move(item));
}

Как я могу решить эту ошибку?


Редактировать

Я изменил конструктор на это:

ListElem(std::unique_ptr<MyObj>&& _item, int _some_counter): item(_item), some_counter(_some_counter) {}

Но теперь я получаю error: use of deleted function

Ответы [ 3 ]

0 голосов
/ 21 января 2019

Инициализация структуры напрямую без конструктора называется агрегатной инициализацией.Это работает только в некоторых случаях.Например,класс не должен иметь никакого конструктора сам по себе (по умолчанию это нормально).Похоже, что некоторые ограничения были сняты в c ++ 14, поэтому, как указано в другом ответе, он отлично работает в c ++ 14 или более поздней версии.Но не в с ++ 11.https://gcc.godbolt.org/z/ZTFUz3

  ListElem item{
      std::unique_ptr<MyObj>(new MyObj2()),
      10
  };

Для вашего редактирования причиной сбоя является то, что вы пропустили перемещение unique_ptr.Добавьте std::move, и он должен нормально работать в обеих версиях.

https://gcc.godbolt.org/z/uFmZvC

ListElem(std::unique_ptr<MyObj>&& _item, int _some_counter)
: item(std::move(_item)), 
some_counter(_some_counter) 
{}
0 голосов
/ 21 января 2019

Проблема заключается в инициализации члена some_counter : это не позволяет выполнить совокупную инициализацию структуры, отметьте здесь https://en.cppreference.com/w/cpp/language/aggregate_initialization, объяснено, что класс (структура) не должен иметь нет инициализаторов членов по умолчанию (с C ++ 11 до C ++ 14).

Здесь код изменился:

// Example program
#include <iostream>
#include <memory>
#include <vector>

class MyObj {
    public:
        virtual ~MyObj(){}
};

class MyObj2 : public MyObj{
    public:
        virtual ~MyObj2(){}
};

struct ListElem {
    std::unique_ptr<MyObj> item;
    int some_counter;

    ListElem() = default;

    ListElem(ListElem const& e) = delete;
    ListElem& operator=(ListElem const& e) = delete;

    ListElem(ListElem&& e) = default;
    ListElem& operator=(ListElem&& e) = default;
};

int main()
{
  std::vector<ListElem> elems;

  ListElem item { std::unique_ptr<MyObj>(new MyObj2()), 10};

  elems.push_back(std::move(item));
}

Ссылка на код: https://wandbox.org/permlink/InZQwGKFyWKmCJrL

0 голосов
/ 21 января 2019

Вы можете удалить все объявления конструктора из вашего класса (1) .Класс тогда будет агрегатным и будет инициализироваться с агрегатной инициализацией (что вы пытаетесь сделать).Обратите внимание, что нет необходимости вручную удалять конструктор копирования или использовать конструктор перемещения по умолчанию, оба будут происходить автоматически.

[Живой пример]

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


(1) Имейте в виду, что struct и class оба представляют типы классов.Единственная разница между ними - неявное управление доступом для баз и членов (public и private соответственно);в остальном они идентичны.

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