Оператор копирования, определенный в шаблоне, удаляемом компилятором

Я знаком с принципом (например, из этого и этого ответа), что когда класс имеет конструктор перемещения и / или оператор присваивания перемещения, его конструктор копирования по умолчанию и оператор назначения копирования удаляются. Тем не менее, в примерах, которые я видел, это можно решить, явно указав новый конструктор копирования и оператор присваивания.

В моем конкретном случае у меня есть класс, который получается путем совместного наследования от структуры в стиле 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 и определить его как дефолт. Это занимает одну дополнительную строку кода.

Другие вопросы по тегам