问题
I would have expected this static assertion to fire:
#include <type_traits>
#include <memory>
int main() {
static_assert(std::is_copy_constructible<std::unique_ptr<int>>::value, "UPtr has copy constructor?");
}
But it does not.
Compiled using MSVC12:
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x64
回答1:
The static_assert
should fire, std::unique_ptr has an implicitly deleted copy constructor, so this is a bug. This looks related to this bug report std::is_copy_constructible is broken:
(1) std::is_copy_constructible returns true for types with deleted copy constructors.
(2) std::is_copy_constructible returns true for types that compose types that are not copy constructible.
and the response was:
Thanks for reporting this bug. We've fixed it, and the fix will be available in the next major version of Visual Studio after 2013.
Also, see this bug report: std::is_copy_constructible doesn't work correctly.
Note that the assert fires on webcompiler which is using an up to date version of Visual Studio. The last update was on Dec 3, 2015
. The assert also fires on clang(see it live) and gcc.
I found a bug report: A strange behavior of std::is_copy_constructible which has very similar code to yours:
static_assert(std::is_copy_constructible<std::unique_ptr<int>>::value, "");
The response there is:
Thanks for reporting this bug. We've already fixed it, and the fix is available in VS 2015 Preview.
Not clear, what version of Visual Studio this is fixed in. One response says late 2013 version while the later on says 2015 Preview.
回答2:
Here are four ways to make class non-copyable:
#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);
}
On MSVC2013 x64 (18.00.40629 for x64
), it prints:
1 1 //A
0 1 //B
1 0 //C
0 0 //D
On a proper compiler, all eight values must be zeroes.
Unfortunately, this does not provide a good way to workaround the bug in MSVC2013, even for your own classes. Because if you declare assignment operator accepting argument by value, then you cannot declare move assignment in the same class (any move assignment will not compile due to ambiguous overload).
P.S. The key idea for fixing assignment was taken from this related answer.
来源:https://stackoverflow.com/questions/34135409/why-does-is-copy-constructible-return-true-for-unique-ptr-in-msvc12