У меня есть интерфейс
IList<T>
, поддерживающий операции добавления копированием и перемещением:
Шаблон IListnamespace lib {
template<class T>
class IList {
public:
virtual void push(const T & data) = 0;
virtual void push(T && data) = 0;
virtual ~IList() = default;
};
}
И есть класс
LinkedList<T>
, который этот интерфейс реализует. Логика обычного односвязного списка: есть внутренний класс
Node
, который просто хранит данные и который также поддерживает как конструктор копирования, так и конструктор перемещения, и два метода push: один копирует передаваемые данные, другой их перемещает, то есть во втором случае копии T создаваться не будет.
Шаблон LinkedListnamespace lib {
template<class T>
class LinkedList : public IList<T> {
private:
class Node { // внутренний класс листа
public:
Node* next = nullptr;
T data;
public:
explicit Node(const T & data) : data(data) {}; // копирование
explicit Node(T && data) : data(std::move(data)) {}; // перемещение
};
private:
Node* head;
Node* tail;
int len;
public:
LinkedList() :
head(nullptr),
tail(nullptr),
len(0) {}; // конструктор листа
void push(const T & data) {
std::cout << "LinkedList::push(const &)" << std::endl;
Node* tmp = new Node(data); // вызывает копирующий конструктор Node
if (this->tail == nullptr) {
this->head = this->tail = tmp;
} else {
this->tail->next = tmp;
this->tail = tmp;
}
++len;
}
void push(T && data) {
std::cout << "LinkedList::push(&&)" << std::endl;
Node* tmp = new Node(std::move(data)); // вызывает конструктор перемещения Node
if (this->tail == nullptr) {
this->head = this->tail = tmp;
} else {
this->tail->next = tmp;
this->tail = tmp;
}
++len;
}
};
}
Также у меня есть класс Item, для которого запрещены любые операции копирования
Шаблон Itemtemplate<class Data>
class Item {
private:
int version;
public:
Item() = default;
Item(int version) : version(version) {}
private:
Item(const Item & item) = default;
Item& operator=(const Item & item) = default;
public:
Item(Item && item) = default;
Item& operator=(Item && item) = default;
};
По задумке, для объекта типа
lib::LinkedList<Item<int>> list = lib::LinkedList<Item<int>>()
мы будем обязаны вызвать
list.push()
только для r-value значения, в ином случае выкинется ошибка. Но просто при вызове конструктора
lib::LinkedList<Item<int>>()
компилятор уже выкидывает ошибку
lib/LinkedList.h: In instantiation of 'lib::LinkedList<T>::Node::Node(const T&) [with T = Item<int>]':
lib/LinkedList.h:35:25: required from 'void lib::LinkedList<T>::push(const T&) [with T = Item<int>]'
main.cpp:24:1: required from here
lib/LinkedList.h:16:54: error: 'Item<Data>::Item(const Item<Data>&) [with Data = int]' is private within this context
explicit Node(const T & data) : data(data) {};
^
In file included from src/KeySpace1.h:6:0,
from main.cpp:6:
src/Item.h:20:5: note: declared private here
Item(const Item & node) = default;
^~~~
То есть он говорит, что не может создать list, потому что при его создании вызывается
explicit Node(const T & data) : data(data) {}
, который в свою очередь выполняет операцию копирования
T data (T = Item<int>)
. Но почему этот конструктор вообще вызывается? При вызове конструктора
lib::LinkedList<Item<int>>()
я вообще не вызываю ни один конструктор
Node
.
Самое забавное, что если закомментировать в
IList
объявления
virtual void push(const T & data) = 0
и
virtual void push(T && data) = 0
, то ошибки не возникает.
Что вообще происходит?