Почему is_copy_constructible возвращает true для unique_ptr в MSVC12
Я бы ожидал, что это статическое утверждение будет срабатывать:
#include <type_traits>
#include <memory>
int main() {
static_assert(std::is_copy_constructible<std::unique_ptr<int>>::value, "UPtr has copy constructor?");
}
Но это не так.
Скомпилировано с использованием MSVC12:
Microsoft (R) C/С++ Оптимизация компилятора Версия 18.00.31101 для x64
Ответы
Ответ 1
static_assert
должен срабатывать, std:: unique_ptr имеет неявно удаленный экземпляр копии, поэтому это ошибка. Это выглядит связанным с этим сообщением об ошибке std:: is_copy_constructible не работает:
(1) std:: is_copy_constructible возвращает true для типов с удаленным копировать конструкторы.
(2) std:: is_copy_constructible возвращает true для типов, которые составляют типы, которые не являются копируемыми.
и ответ был:
Спасибо за сообщение об этой ошибке. Мы исправили его, и исправление будет доступно в следующей крупной версии Visual Studio после 2013 года.
Также см. отчет об ошибке: std:: is_copy_constructible не работает правильно.
Обратите внимание, что assert запускает webcompiler, в котором используется обновленная версия Visual Studio. Последнее обновление было на Dec 3, 2015
. Утверждение также срабатывает на clang (видеть его в прямом эфире) и gcc.
Я нашел отчет об ошибке: Странное поведение std:: is_copy_constructible, которое имеет очень похожий код для вашего:
static_assert(std::is_copy_constructible<std::unique_ptr<int>>::value, "");
Ответ:
Спасибо за сообщение об этой ошибке. Мы уже исправили это, и исправление доступно в предварительном просмотре VS 2015.
Неясно, какая версия Visual Studio исправлена. Один ответ говорит о версии конца 2013 года, а позже - о предпросмотре 2015 года.
Ответ 2
Вот четыре способа сделать класс несовместимым:
#include <stdio.h>
#include <type_traits>
class A {
public:
A(const A&) = delete;
void operator=(const A&) = delete;
};
class B {
private:
B(const B&) = delete;
void operator=(const B&) = delete;
};
class C {
public:
C(const C&) = delete;
void operator=(const C&) = delete;
void operator=(C) = delete;
};
class D {
private:
D(const D&) = delete;
void operator=(const D&) = delete;
void operator=(D) = delete;
};
int main() {
printf("%d %d\n", std::is_copy_constructible<A>::value, std::is_copy_assignable<A>::value);
printf("%d %d\n", std::is_copy_constructible<B>::value, std::is_copy_assignable<B>::value);
printf("%d %d\n", std::is_copy_constructible<C>::value, std::is_copy_assignable<C>::value);
printf("%d %d\n", std::is_copy_constructible<D>::value, std::is_copy_assignable<D>::value);
}
В MSVC2013 x64 (18.00.40629 for x64
) он печатает:
1 1 //A
0 1 //B
1 0 //C
0 0 //D
В правильном компиляторе все восемь значений должны быть нулями.
К сожалению, это означает, что не обеспечивает хороший способ обхода ошибки в MSVC2013, даже для ваших собственных классов. Потому что, если вы объявляете оператор присваивания, принимающий аргумент по значению, то вы не можете объявить назначение переноса в одном классе (любое присваивание перемещения не будет компилироваться из-за неоднозначной перегрузки).
P.S. Основная идея для фиксации присвоения взята из этого связанного ответа.