11

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

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
sji
  • 1,877
  • 1
  • 13
  • 28
  • 1
    Odd. On GCC 5 (Linux), the assert fires. – hlt Dec 07 '15 at 14:04
  • 1
    It fails with g++: http://coliru.stacked-crooked.com/a/d0592d5e7824f2cd – NathanOliver Dec 07 '15 at 14:04
  • Fails on [webcompiler](http://webcompiler.cloudapp.net/) which as far as I know is relatively up to date visual studio. – Shafik Yaghmour Dec 07 '15 at 14:05
  • Fair enough, just a compiler bug on our version then! Unfortunately we can't just upgrade. Easy to work around but it just seemed odd. – sji Dec 07 '15 at 14:07
  • 5
    @RichardHodges I like where I work thanks. Please consider that there might be a good reason for not upgrading right now before you insult other peoples workplaces. – sji Dec 07 '15 at 15:06
  • Also related: https://stackoverflow.com/questions/27620277/msvc-is-copy-assignable-always-true – stgatilov Mar 04 '18 at 07:03

2 Answers2

15

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.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Didn't know about that web-compiler. Will be a handy tool thanks. – sji Dec 07 '15 at 15:05
  • "std::unique_ptr is neither CopyConstructible nor CopyAssignable" - Let's not confuse CopyConstructible and CopyAssignable (the concepts) with what is_copy_constructible etc. checks. The former is much stronger than the latter. – T.C. Dec 09 '15 at 15:31
  • @T.C. I see how that could be confusing, fixed. – Shafik Yaghmour Dec 09 '15 at 15:58
0

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.

stgatilov
  • 5,333
  • 31
  • 54