Why not using move semantics in the following snippet?

Viewed 60

I am trying to understand C++ "move semantics" using the folliwing snippet, and I am using Visual Studio 2019 with C++ 14 standard.

    #include <cstring>
#include <algorithm>
#include <iostream>

class string
{
    char* data;

public:

    string(const char* p)
    {
        size_t size = std::strlen(p) + 1;
        data = new char[size];
        std::memcpy(data, p, size);
    }

    ~string()
    {
        delete[] data;
    }

    string(const string& that)
    {
        std::cout << "copy called" << std::endl;
        size_t size = std::strlen(that.data) + 1;
        data = new char[size];
        std::memcpy(data, that.data, size);
    }

    string(string&& that) noexcept // string&& is an rvalue reference to a string
    {
        std::cout << "move called" << std::endl;
        data = that.data;
        that.data = nullptr;
    }

};

string getStr() {
    return string("hello");
}

int main()
{
    auto s = getStr();
}

I expect move copy construct called here string(string&& that) noexcept, but it turns out not. Anyone could help me understand the reason why no move copy constructor called here. thanks!

1 Answers

This is because of RVO. I have added a cout to see what is going on. You can check out the code output here

  #include <cstring>
#include <algorithm>
#include <iostream>

class string
{
    char* data;

public:

    string(const char* p)
    {
        size_t size = std::strlen(p) + 1;
        data = new char[size];
        std::memcpy(data, p, size);
        std::cout<<"const char * called"<<std::endl;
    }

    ~string()
    {
        delete[] data;
    }

    string(const string& that)
    {
        std::cout << "copy called" << std::endl;
        size_t size = std::strlen(that.data) + 1;
        data = new char[size];
        std::memcpy(data, that.data, size);
    }

    string(string&& that) noexcept // string&& is an rvalue reference to a string
    {
        std::cout << "move called" << std::endl;
        data = that.data;
        that.data = nullptr;
    }

};

string getStr() {
    return string("hello");
}

int main()
{
    auto s = getStr();
}

According to cppreference:

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects.

    In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type
Related