Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move Constructors and Static Arrays

I've been exploring the possibilities of Move Constructors in C++, and I was wondering what are some ways of taking advantage of this feature in an example such as below. Consider this code:

template<unsigned int N> class Foo { public:     Foo() {         for (int i = 0; i < N; ++i) _nums[i] = 0;     }      Foo(const Foo<N>& other) {         for (int i = 0; i < N; ++i) _nums[i] = other._nums[i];     }      Foo(Foo<N>&& other) {         // ??? How can we take advantage of move constructors here?     }      // ... other methods and members      virtual ~Foo() { /* no action required */ }  private:     int _nums[N]; };  Foo<5> bar() {     Foo<5> result;     // Do stuff with 'result'     return result; }  int main() {     Foo<5> foo(bar());     // ...     return 0; } 

In this above example, if we trace the program (with MSVC++ 2011), we see that Foo<N>::Foo(Foo<N>&&) is called when constructing foo, which is the desired behaviour. However, if we didn't have Foo<N>::Foo(Foo<N>&&), Foo<N>::Foo(const Foo<N>&) would be called instead, which would do a redundant copy operation.

My question is, as noted in the code, with this specific example which is using a statically-allocated simple array, is there any way to utilize the move constructor to avoid this redundant copy?

like image 411
Zeenobit Avatar asked Oct 25 '11 06:10

Zeenobit


People also ask

What are move constructors?

A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. For more information about move semantics, see Rvalue Reference Declarator: &&. This topic builds upon the following C++ class, MemoryBlock , which manages a memory buffer.

Can static arrays be changed?

Statically declared arrays are allocated memory at compile time and their size is fixed, i.e., cannot be changed later.

Is move constructor automatically generated?

If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.

Why does Noexcept move a constructor?

Tagging our move constructor with "noexcept" tells the compiler that it will not throw any exceptions. This condition is checked in C++ using the type trait function: "std::is_no_throw_move_constructible". This function will tell you whether the specifier is correctly set on your move constructor.


1 Answers

First off, there's a general sort of advice that says you shouldn't write any copy/move constructor, assignment operator or destructor at all if you can help it, and rather compose your class of high-quality components which in turn provide these, allowing the default-generated functions to Do The Right Thing. (The reverse implication is that if you do have to write any one of those, you probably have to write all of them.)

So the question boils down to "which single-responsibility component class can take advantage of move semantics?" The general answer is: Anything that manages a resource. The point is that the move constructor/assigner will just reseat the resource to the new object and invalidate the old one, thus avoiding the (presumed expensive or impossible) new allocation and deep copying of the resource.

The prime example is anything that manages dynamic memory, where the move operation simply copies the pointer and sets the old object's pointer to zero (so the old object's destructor does nothing). Here's a naive example:

class MySpace {   void * addr;   std::size_t len;  public:   explicit MySpace(std::size_t n) : addr(::operator new(n)), len(n) { }    ~MySpace() { ::operator delete(addr); }    MySpace(const MySpace & rhs) : addr(::operator new(rhs.len)), len(rhs.len)   { /* copy memory */ }    MySpace(MySpace && rhs) : addr(rhs.addr), len(rhs.len)   { rhs.len = 0; rhs.addr = 0; }    // ditto for assignment }; 

The key is that any copy/move constructor will do a full copying of the member variables; it is only when those variables are themselves handles or pointers to resources that you can avoid copying the resource, because of the agreement that a moved object is no longer considered valid and that you're free to steal from it. If there's nothing to steal, then there's no benefit in moving.

like image 74
Kerrek SB Avatar answered Oct 19 '22 19:10

Kerrek SB