12

After doing a bit of research, I see that C++11 has a defect with allocators that require the type to be movable/copyable. I'm sure that this is the cause of this problem, however I am confused about the behavior between deleted and not declared move semantics.

I have the following code which fails to compile on both MSVC12 and Clang:

#include <vector>

class Copyable
{
public:
   Copyable() = default;

   Copyable(Copyable const& other)
      : m_int(other.m_int)
   {}

   Copyable& operator= (Copyable const& other)
   {
      m_int = other.m_int;
      return *this;
   }

   Copyable(Copyable&&) = delete;
   Copyable& operator= (Copyable&&) = delete;

private:
   int m_int = 100;
};

int main()
{
   std::vector<Copyable> objects;
   objects.push_back(Copyable{});
}

This fails to compile on MSVC with:

xmemory0(600): error C2280: 'Copyable::Copyable(Copyable &&)' : attempting to reference a deleted function

And on Clang (live sample):

new_allocator.h:120:23: error: call to deleted constructor of 'Copyable'

In both cases, when I remove the explicitly deleted move construct/assign methods, the code compiles. AFAIK when you declare copy assign/construct methods, the compiler does not implicitly declare the corresponding move members. So they should still be effectively deleted, right? Why does the code compile when I remove the explicit deletion of move construct/assign?

What is a good workaround for this C++11 defect in general? I do not want my objects to be movable (but they are copyable).

void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • Implement moving via a copy? – Neil Kirk Oct 21 '14 at 15:02
  • Also, related to : [Why do C++11-deleted functions participate in overload resolution?](http://stackoverflow.com/questions/14085620/why-do-c11-deleted-functions-participate-in-overload-resolution) – Nawaz Oct 21 '14 at 17:03

2 Answers2

15

Deleting a function is not the same as not declaring it.

A deleted function is declared and participates in overload resolution, but if you attempt to call it an error is produced.

If you fail to declare your move constructor, the compiler will not create one as you created a copy constructor. Overload resolution on an rvalue will find your copy constructor, which is probably what you want.

What you said with your foo(foo&&)=delete was "if anyone tries to move construct this object, generate an error".

I can illustrate the difference here:

void do_stuff( int  x ) { std::cout << x << "\n"; }
void do_stuff( double ) = delete;

void do_stuff2( int  x ) { std::cout << x << "\n"; }
//void do_stuff2( double ) = delete;

int main() {
  do_stuff(3); // works
  //do_stuff(3.14); // fails to compile
  do_stuff2(3); // works
  do_stuff2(3.14); // works, calls do_stuff2(int)
}

the only part with your above problem that makes this a bit more confusing is that special member functions are automatically created or not based off slightly arcane rules.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
11
Copyable(Copyable&&) = delete;
Copyable& operator= (Copyable&&) = delete;

Unless you are an expert in move semantics (and I mean, really knowledgeable), never delete the special move members. It won't do what you want. If you review someone else's code that has done this, call it out. The explanation has to be really solid, and not "because I don't want the type to move."

Just don't do it.

The proper way to do what you want is to simply declare/define your copy members. The move members will be implicitly inhibited (not deleted, but actually not there). Just write C++98/03.

For more details, see this answer.

Community
  • 1
  • 1
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    +1 this is the practical advice that is hard to find even though there are a lot of helpful references out there. – Shafik Yaghmour Oct 21 '14 at 19:03
  • 4
    How about writing a little book with all this great stuff (move semantics, parameter passing, class design, dates, permutations and threads) that you have put together here on SO (and used in libc++) over the years? As @ShafikYaghmour correctly states, it's all very hard to find, and scattered. – TemplateRex Oct 21 '14 at 21:18
  • 1
    @TemplateRex: Looking for the time... – Howard Hinnant Oct 22 '14 at 02:34