@HaseProgram
Студент

Как поместить «нетривиальный» объект в стуктуру?

Доброе время суток!
Я не знаю как правильно сформулировать мой вопрос, так как столкнулся с этим впервые, обшарил кучу форумов, но толком так и не понял в чем суть.

У меня есть следующая структура проекта. (В урезаном формате и без реализации методов, вставлю только имеющиеся конструкторы)
class Point : public PointBase
{
public:
	Point(double X, double Y, double Z)
	{
	}

private:
	double X;
	double Y;
	double Z;
};

class Edge : public EdgeBase
{
public:
	Edge(Point* firstPoint, Point* secondPoint)
	{
		this->firstPoint = firstPoint;
		this->secondPoint = secondPoint;
	}
private:
	Point* firstPoint;
	Point* secondPoint;
};

template<typename type_t>
struct item
{
	item* next;
	type_t data;
};

template <typename type_t>
class Array : public ArrayBase
{
public:
	Array();

	friend class IArray<typename type_t>;

private:
	item<type_t> *head;
	item<type_t> *tail;
};

template <class type_t>class Array;

template <class type_t>
class IArray
{
public:
	IArray() {};
	IArray(Array<type_t>&);

private:
	Array<type_t>* arr;
	item<type_t>* currentItem;
};

class Model : public ModelBase
{
public:
	Model();
	Model(const Model& model) 
	{
	};

	Model& operator=(const Model& model)
	{
	}

private:
	IArray<Point> points;
	IArray<Edge> edges;

	Model(Model&&);
};


Далее, например в классе IArray (итератор для класса Array), я хочу сделать функцию добавления элемента.
Но на попытку выделить память
item<type_t>* newItem = new item<type_t>;
выскакивает ошибка C2280 "item::item(void)": attempting to reference a deleted function мол попытка ссылки на удаленную функцию, в качестве функции он требует конструктор у структуры item (?_?)

При добавлении всевозможных конструкторов (вообще впервые на самом деле сталкиваюсь, чтобы структура требовала конструкторы, учитывая, что уже делал подобный проект)
(ограничился этими, не знаю, что еще можно добавить)
template<typename type_t>
struct item
{
	item* next;
	type_t data;
	item() {};
	item(const item<type_t>&) {};
	item(item<type_t>&&) {};
};

Выскакивает ошибка, что не найден конструктор подходящий конструктор по умолчанию для Edge (для Point типа найден ~_~)
В чем собственно тут дело я без понятия, предположение только в том, что в Edge по сути у нас хранится еще один объект (правда там только указатель на него), хотя не знаю как это может влиять.
Буду очень признателен, если кто-то поможет разобраться в этой ситуации, и подсказать возможное ее решение. Заранее спасибо!
  • Вопрос задан
  • 264 просмотра
Решения вопроса 1
@Mercury13
Программист на «си с крестами» и не только
Одно из полей (в данном случае data в реализации item<Edge>) не имеет конструктора по умолчанию. Есть три пути.

РАЗ. Придумать, как сделать, чтобы конструктор всё-таки был.
class Edge : public EdgeBase
{
public:
  Edge (Point* firstPoint, Point* secondPoint) { init(firstPoint, secondPoint); }
  Edge () { init(NULL, NULL); }
  void init (Point* firstPoint, Point* secondPoint)
  {
    this->firstPoint = firstPoint;
    this->secondPoint = secondPoint;
  }
private:
  Point* firstPoint;
  Point* secondPoint;
};


ДВА. Если объект копируется/переносится, можно воспользоваться такой штукой. С вашего позволения, упрощу задачу и вместо Edge заведу другой класс — ImmutableInt. Правда, этот код — попытка совместить ежа с ужом (непонятно, какую концепцию должен поддерживать Payload), но, тем не менее, работает.
#include <iostream>

class ImmutableInt
{
public:
    explicit ImmutableInt(int x) : fData(x) {}
    int data() const { return fData; }
private:
    int fData;
};

template <class Payload>
class ListItem
{
public:
    Payload payload;
    ListItem* next;

    ListItem() : next(NULL) {}
    ListItem(const Payload& x) : payload(x), next(NULL) {}
};

// Так писать нельзя — эта конструкция расшаблонивает все функции,
// а конструктора по умолчанию как не было, так и нет!
//template class ListItem<ImmutableInt>;

int main()
{
    ListItem<ImmutableInt> li(ImmutableInt(42));
    std::cout << li.payload.data() << std::endl;
    return 0;
}


ТРИ. Воспользоваться переменными шаблонами (variadic templates) C++11.
#include <iostream>

class ImmutableInt
{
public:
    explicit ImmutableInt(int x) : fData(x) {}
    int data() const { return fData; }
private:
    int fData;
};

template <class Payload>
class ListItem
{
public:
    Payload payload;
    ListItem* next = nullptr;

    template<class... Args>ListItem(Args... args) : payload(args...) {}
};

int main()
{
    ListItem<ImmutableInt> li(42);
    std::cout << li.payload.data() << std::endl;
    return 0;
}


P.S. Хотя ImmutableInt — семантически тот же int и теоретически explicit не надо, всё-таки отметил — просто чтобы показать, что во втором случае мы передаём параметром ImmutableInt<42>, а в третьем — 42.

P.P.S. Я упомянул слово «концепция». Это набор требований к типу. Что-то типа интерфейса — но, во-первых, никак не связано с ООП и его динамическим полиморфизмом, и, во-вторых, не столь жёсткое: как ты интерфейсом из Java наладишь концепцию «есть конструктор копирования» или «может делить себя на число и что-то выходит»? Синтаксическая поддержка концепций откладывается на C++17, но уже в C++03 было несколько концепций: InputIterator, DefaultConstructible, CopyAssignable и другие. А пока… концепция не поддерживается — ошибка компиляции где-то в страшенном стандартном хедере.

P.P.P.S. Написав код
template<typename type_t>
struct item
{
  item* next;
  type_t data;
};

вы автоматически потребовали от type_t концепцию DefaultConstructible (есть конструктор по умолчанию)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы