Оператор копирования, определенный в шаблоне, удаляемом компилятором
Я знаком с принципом (например, из этого и этого ответа), что когда класс имеет конструктор перемещения и / или оператор присваивания перемещения, его конструктор копирования по умолчанию и оператор назначения копирования удаляются. Тем не менее, в примерах, которые я видел, это можно решить, явно указав новый конструктор копирования и оператор присваивания.
В моем конкретном случае у меня есть класс, который получается путем совместного наследования от структуры в стиле C и шаблонного класса. Операторы присваивания копирования и перемещения явно определены в шаблоне, а конструкторы копирования и перемещения явно определены в самом классе. Другими словами, все определено явно, но не все в одном месте. Вот пример кода:
typedef struct {
int n;
} myStruct;
template <typename T> class myTemplate
{
public:
// Default constructor
myTemplate<T>() : t_n(nullptr) {}
// Cannot create copy or move constructors in template, as cannot
// access the 'n' member directly
// Copy assignment operator
myTemplate<T> & operator=(const myTemplate<T> &source)
{
if (this != &source)
{
*t_n = *(source.t_n);
}
return *this;
}
//! Move assignment operator
myTemplate<T> & operator=(myTemplate<T> &&source)
{
if (this != &source)
{
*t_n = *(source.t_n);
*(source.t_n) = 0;
source.t_n = nullptr;
}
return *this;
}
T* t_n;
};
class myClass : public myStruct, public myTemplate<int>
{
public:
// Default constructor
myClass() : myTemplate<int>()
{
n = 0;
t_n = &n;
}
// Alternative constructor
myClass(const int &n_init) : myTemplate<int>()
{
n = n_init;
t_n = &n;
}
// Copy constructor
myClass(const myClass &source) : myTemplate<int>()
{
n = source.n;
t_n = &n;
}
// Move constructor
myClass(myClass &&source) : myTemplate<int>()
{
n = source.n;
t_n = &n;
source.n = 0;
source.t_n = nullptr;
}
};
int main()
{
myClass myObject(5);
myClass myOtherObject;
// Compilation error here:
myOtherObject = myObject;
return 1;
}
В Visual C++ и Intel C++ в Windows это работает именно так, как я ожидал. На gcc 4.9.0 в Linux, однако, я получаю страшное сообщение об ошибке:
g++ -c -std=c++11 Main.cppMain.cpp: In function ‘int main()’:
Main.cpp:78:19: error: use of deleted function ‘myClass& myClass::operator=(const myClass&)’
myOtherObject = myObject;
^
Main.cpp:39:7: note: ‘myClass& myClass::operator=(const myClass&)’ is implicitly declared as deleted because ‘myClass’ declares a move constructor or move assignment operator
class myClass : public myStruct, public myTemplate<int>
Конечно же, ошибка исчезнет, если я определю явный оператор назначения копирования в самом классе, а не в шаблоне, но это утомительно и подрывает преимущество использования шаблона, поскольку (а) мой фактический оператор назначения копирования намного больше, чем показанный здесь, и (b) существует большое количество различных классов, которые все используют этот шаблон.
Так это просто ошибка в gcc 4.9.0, или это то, что стандарт должен сказать?
1 ответ
GCC правильно (и Clang и EDG согласны).
myTemplate
имеет объявленный пользователем конструктор перемещения, поэтому оператор копирования его удаляется.
Вы предоставили конструктор копирования, но не оператор копирования. Просто объявите оператор присваивания копии для myTemplate
и определить его как дефолт. Это занимает одну дополнительную строку кода.