4
#include <type_traits>

class Test
{
public:
    Test(const Test &) = delete;
    Test &operator=(const Test &) = delete;
};

void fn(Test &a, const Test &b) { a = b; }

static_assert(!std::is_copy_assignable<Test>::value, "Test shouldn't be assignable");

Compiling this under MSVC 2013 Update 3 unexpectedly fails the static_assert, and the function fn fails to compile (as expected.) This is contradictory, right?

Am I misusing is_copy_assignable? Is there another way to test for this condition?

Tom Whittock
  • 4,081
  • 19
  • 24
  • Where is that `static_assert` in your code? – Jonathan Mee Dec 23 '14 at 12:33
  • The code I'm compiling is exactly as it appears here. My original code used `is_copy_assignable` as a parameter in `enable_if`, this is the reduction I've created to try to diagnose the issue. – Tom Whittock Dec 23 '14 at 12:36
  • 1
    http://en.cppreference.com/w/cpp/language/static_assert: "A static assert declaration may appear at block scope (as a block declaration) and inside a class body (as a member declaration)" – Jonathan Mee Dec 23 '14 at 12:38
  • static_assert is a declaration, it can be used in a namespace scope too. Anyway, putting a dummy function around the assertion doesn't help - it still asserts. – Tom Whittock Dec 23 '14 at 12:43
  • possible duplicate of [MSVC - How can I find out if a type must be moved?](http://stackoverflow.com/questions/15396193/msvc-how-can-i-find-out-if-a-type-must-be-moved) – Jonathan Mee Dec 23 '14 at 13:00

2 Answers2

3

You are correct this is a bug: https://connect.microsoft.com/VisualStudio/feedback/details/819202/std-is-assignable-and-std-is-constructible-give-wrong-value-for-deleted-members

I took cplusplus.com's is_copy_assignable code:

#include <iostream>
#include <type_traits>

struct A { };
struct B { B& operator= (const B&) = delete; };

int main() {
    std::cout << std::boolalpha;
    std::cout << "is_copy_assignable:" << std::endl;
    std::cout << "int: " << std::is_copy_assignable<int>::value << std::endl;
    std::cout << "A: " << std::is_copy_assignable<A>::value << std::endl;
    std::cout << "B: " << std::is_copy_assignable<B>::value << std::endl;
    return 0;
}

And tested it on Visual Studio 2013 and got:

is_copy_assignable:
int: true
A: true
B: true

On gcc 4.8.1 I got:

is_copy_assignable:
int: true
A: true
B: false

Notably on the Visual Studio 2015 Beta this is fixed. I get:

is_copy_assignable:
int: true
A: true
B: false

How do you feel about betas ;)

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • Thanks, I'll have to do some sort of sfinae workaround I guess. 2015 isn't going to happen for me any time soon. – Tom Whittock Dec 23 '14 at 13:35
  • @TomWhittock I linked a possible duplicate, one of the guys offers a work around in there, you might wanna check it out. – Jonathan Mee Dec 23 '14 at 13:40
  • 1
    We hit that at work as well; there was a different ms connect for it - if you just mark the copy constructor private (C++98 emulation of deleting) the trait still returns true; basically access modifiers were not taken into account. – Stefan Atev Dec 23 '14 at 15:50
  • @StefanAtev Just checked making the copy constructor private does work on Visual Studio 2015 and gcc 4.8.1. That's probably not a surprise. Did you have a work around that you guys came up with? That would probably make a better answer than "update your compiler to the Beta". – Jonathan Mee Dec 23 '14 at 16:00
  • 1
    @JonathanMee - He-he; the issue linked above was sent by my colleague; we found no workaround - we changed the code so we didn't need the trait; we couldnt't SFINAE our way to a working trait so I can't help much. – Stefan Atev Dec 23 '14 at 16:56
  • @StefanAtev So what you're saying is use Visual Studio 2015 or a different compiler all together. BILL GATES![shakes fist] – Jonathan Mee Dec 23 '14 at 16:59
  • @JonathanMee - I'm saying the possible duplicate you linked to may not ICE for you so there's a tiny glimmer of hope; unfortunately over the years I have witnessed a lot of ms connects resolved with "switch to a beta version that is two releases ahead of what you use in production (and changes the build system)". – Stefan Atev Dec 23 '14 at 17:02
2

May be a bit too late, but I have a workaround here: define another copy-and-move assignment operator, also as a deleted one. The compiler gives a warning, complaining about multiple copy operators (C4522), so you have to disable it for the class:

#pragma warning (disable: 4522)
class A
{
    A& operator=(const A&) = delete;
    A& operator=(A) = delete;
};
#pragma warning (default: 4522)

Now std::is_copy_assignable<A>::value is false.

Lao
  • 222
  • 1
  • 8
  • If you declare assignment-by-value operator in a class, then you cannot declare move assignment operator there. This limitation makes your workaround rather useless, given that type traits are often used for move-only classes like `std::unique_ptr` which must have move assignment operator working. – stgatilov Mar 04 '18 at 13:04