Ошибка компилятора С++ C2280 "попытка ссылки на удаленную функцию" в Visual Studio 2013 и 2015
Этот фрагмент скомпилирован без ошибок в Visual Studio 2013 (версия 12.0.31101.00 Update 4)
class A
{
public:
A(){}
A(A &&){}
};
int main(int, char*)
{
A a;
new A(a);
return 0;
}
пока он скомпилирован с этой ошибкой в Visual Studio 2015 RC (версия 14.0.22823.1 D14REL):
1>------ Build started: Project: foo, Configuration: Debug Win32 ------
1> foo.cpp
1>c:\dev\foo\foo.cpp(11): error C2280: 'A::A(const A &)': attempting to reference a deleted function
1> c:\dev\foo\foo.cpp(6): note: compiler has generated 'A::A' here
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Я думаю, что компилятор, поставляемый с Visual Studio 2015, генерирует Copy Constructor и отмечает его как =delete
, и поэтому я получаю ошибку C2280 (которая, кстати, я не могу найти документально на msdn.microsoft.com).
Теперь скажем, что у меня есть кодовая база, которая компилируется с Visual Studio 2013 (и она работает, потому что она опирается на код, сгенерированный автоматически компилятором), но не компилируемая с Visual Studio 2015 из-за C2280, как я могу исправить проблема?
Я думал объявить класс A
следующим образом:
class A
{
public:
A(){}
A(A &&){}
A(const A&)=default;
};
Мне что-то не хватает?
Ответы
Ответ 1
Из [class.copy]/7, мой удар:
Если определение класса явно не объявляет конструктор копирования, неявный объявляется неявным образом. Если определение класса объявляет конструктор перемещения или перемещает оператор присваивания, неявно объявленная копия конструктор определяется как удаленный; в противном случае он определяется как дефолт (8.4). Последний случай устарел, если класс имеет пользовательский оператор присваивания копии или объявленный пользователем деструктор.
Существует эквивалентный раздел с аналогичной формулировкой для назначения копирования в пункте 18. Таким образом, ваш класс действительно:
class A
{
public:
// explicit
A(){}
A(A &&){}
// implicit
A(const A&) = delete;
A& operator=(const A&) = delete;
};
поэтому вы не можете его копировать. Если вы предоставляете конструктор/назначение перемещения, и вы все еще хотите, чтобы класс был скопирован, вам придется явно предоставить эти специальные функции-члены:
A(const A&) = default;
A& operator=(const A&) = default;
Вам также необходимо объявить оператор присваивания переадресации. Если вам действительно нужны эти специальные функции, вам также, вероятно, понадобится деструктор. См. Правило пяти.
Ответ 2
У меня была та же проблема, и это было связано с плохо определенной переменной-членом:
double const deltaBase = .001;
Ввод этого параметра приведет к удалению конструктора копии. Избавьтесь от "const" и присвойте в конструкторе.
Ответ 3
Если вы напишете определяемый пользователем конструктор перемещения для своего класса, конструктор копирования будет удален. Это связано с тем, что если классу требуется специальное поведение для его конструктора перемещения, ему, вероятно, потребуется некоторое сходное поведение в его конструкторе копирования, поэтому конструктор копирования будет удален, чтобы вы случайно не использовали поведение по умолчанию.
Если вы хотите определить свой собственный конструктор перемещения и использовать конструктор копии по умолчанию, вам нужно объявить его как default
, как вы сказали в своем вопросе:
class A
{
public:
A(){}
A(A &&){}
//I know what I'm doing, compiler, use the default version.
A(const A&)=default;
};
Обратите внимание, что если вы определяете собственный конструктор перемещения, вы должны также подумать о своих операторах присваивания и деструкторе.
Ответ 4
Я застрял с этой ошибкой даже после "по умолчанию" копирования ctor. Оказалось, что один из моих учеников (объект Документа quickjson) запрещал копирование. Изменил его на ссылку, инициализированную с помощью * (new rapidjson :: Document()) в списке инициализатора ctor по умолчанию. Похоже, что все отдельные элементы также должны быть копируемыми в дополнение к ctor по умолчанию.