Search code examples
c++movemove-semantics

How to implement the move function in C++?


I want to know the internals of the move function in C++. For that, I aim to make my own implementation of the move function called move2.

Here is my implementation with prints to track memory allocation.

#include <iostream>
#include <vector>

using namespace std;
void * operator new(size_t size) {
    cout << "New operator overloading " << endl;
    void * p = malloc(size);
    return p;
}

void operator delete(void * p) {
    cout << "Delete operator overloading " << endl;
    free(p);
}

template<typename T>
T move2(T& input) {
    cout << "Running move2 " << endl;
    return (T&&)input;
}

int main()
{
    {
        cout<<"Test#1"<<endl;
        vector<int> temp1(10,4);
        vector<int> temp2 = temp1;
    }
    {
        cout<<"Test#2"<<endl;
        vector<int> temp3(10,4);
        vector<int> temp4 = (vector<int>&&)(temp3);
    }
    {
        cout<<"Test#3"<<endl;
        vector<int> temp5(10,4);
        vector<int> temp6 = move(temp5);
    }
    {
        cout<<"Test#4"<<endl;
        vector<int> temp7(10,4);
        vector<int> temp8 = move2(temp7);
    }
    return 0;
}

Here is the output

Test#1
New operator overloading
New operator overloading
Delete operator overloading
Delete operator overloading
Test#2
New operator overloading
Delete operator overloading
Test#3
New operator overloading
Delete operator overloading
Test#4
New operator overloading
Running move2
Delete operator overloading

I want to know if my implementation of move2 is correct and can I use it in production?

Update:

I found the GCC implementation of move

  /**
   *  @brief  Convert a value to an rvalue.
   *  @param  __t  A thing of arbitrary type.
   *  @return The parameter cast to an rvalue-reference to allow moving it.
  */
  template<typename _Tp>
    constexpr typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t) noexcept
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

Solution

  • std::move() takes in a forwarding reference, and returns a T&& rvalue reference. Your move2() does neither. It takes in an lvalue reference, and returns a T by value.

    Your code "works" due to copy elision avoiding a temporary object being created when your function returns a new object by value, thus allowing temp8 to be constructed directly with your type-casted reference to input, thus transferring ownership of its data. That is not because of a proper move2() implementation, though.

    Try this instead:

    template<typename T>
    std::remove_reference_t<T>&& move2(T&& input) {
        ...
        return static_cast<std::remove_reference_t<T>&&>(input);
    }