Home > database >  Why use std::swap instead of assignment for built-in types in a move assignment operator?
Why use std::swap instead of assignment for built-in types in a move assignment operator?

Time:09-20

I'm a C newbie, and this is probably a stupid question to ask.

I'm reading a book Data Structures and Algorithm Analysis in C , where the implementation of a user-defined vector class template was provided.

Here's a snippet of it

// Move Constructor
Vector(Vector&& source) noexcept
    : size(source.size), capacity(source.capacity), objects(source.objects)
{
    source.objects = nullptr;
    source.size = 0;
    source.capacity = 0;
}

// Move Assignment
Vector & operator= (Vector&& source) noexcept
{
    std::swap(size, source.size);
    std::swap(capacity, source.capacity);
    std::swap(objects, source.objects);

    return *this;
}

I figure some operations are redundant, here's my draft↓

// Move Constructor
Vector(Vector&& source) noexcept
    : size(source.size), capacity(source.capacity), objects(source.objects)
{
    source.objects = nullptr; // Forget the rest two integers
}

// Move Assignment
Vector & operator= (Vector&& source) noexcept
{
    size = source.size; // std::swap is unnecessary
    capacity = source.capacity;
    std::swap(objects, source.objects);

    return *this;
}

Why would the author use std::swap here? To my knowledge, std::swap is implemented using move semantics, which is introduced to fix smart pointers, not for basic data types.

size and capacity are merely integers, std::swap actually calls std::move and does the assignment three times, thus slowing down the performance since an ordinary assignment would do the same thing. Or is there any benefit of doing so?

Re: Much obliged for the comments folks, I can understand that there is no penalty for using std::move, but swapping means three assignments, right? Can I just use a = operator instead?

CodePudding user response:

The author of this code makes the entirely reasonable assumption that the implementation has a decent implementation of std::swap<size_t>.

There are a number of reasons why this can be the case, but the two major reasons are:

  1. The optimizer will likely put the involved variables in registers, making the swap effectively free. Compilers track which register holds which value; so you the compiler can swap its associated bookkeeping without swapping physical registers. This is a pure compile-time operation
  2. If this wouldn't have worked, the library implementation can specialize std::swap<size_t> and other critical algorithms. You probably looked at the non-specialized template.

CodePudding user response:

I believe it might be possible to implement a vector-like class template such that the pointer data objects being nullptr and size and capacity being nonzero would be a valid vector state. However, this would bring significant overhead into almost all vector operations. Consider just a simple push_back (I appended underscore to the private member variables for clarity):

size_t size() const 
{
  return objects_ == nullptr ? 0 : size_;
}

size_t capacity() const
{
  return objects_ == nullptr ? 0 : capacity_;
}

void push_back(const T& value)
{
  if (size() == capacity()) // branching overhead here
    // preform reallocation first
  ...

If we instead insist being all objects, size, and capacity in a consistent state, this would simply be:

void push_back(const T& value)
{
  if (size_ == capacity_) // no unnecessary branching here
  ...
  • Related