2

In the following C++ code, there should be three heap allocations as in the swap() function one temporary string object is also created. Why there are only two heap allocations in this code?

Without using move semantics

#include <iostream>
#include <unordered_map>

using namespace std;


static uint32_t allocations = 0;

void *operator new(size_t size)
{
    allocations++;
    cout << "Allocating " << size << " bytes\n";
    return malloc(size);
}


void swapUsingMove(string& arg1, string& arg2)
{
    string temp = arg1;
    arg1 = arg2;
    arg2 = temp;
    cout << allocations << endl;
}

int main()
{
    string str1{"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
    string str2{"ZYXWVUTSRQPONMLKJIHGFEDCBA"};

    swapUsingMove(str1, str2);
    cout << str1 << " " << str2;
    return 0;
}

Output

Allocating 51 bytes
Allocating 51 bytes
2
ZYXWVUTSRQPONMLKJIHGFEDCBA ABCDEFGHIJKLMNOPQRSTUVWXYZ

By using move semantics

#include <iostream>
#include <unordered_map>

using namespace std;


static uint32_t allocations = 0;

void *operator new(size_t size)
{
    allocations++;
    cout << "Allocating " << size << " bytes\n";
    return malloc(size);
}


void swapUsingMove(string& arg1, string& arg2)
{
    string temp = move(arg1);
    arg1 = move(arg2);
    arg2 = move(temp);
    cout << allocations << endl;
}

int main()
{
    string str1{"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
    string str2{"ZYXWVUTSRQPONMLKJIHGFEDCBA"};

    swapUsingMove(str1, str2);
    cout << str1 << " " << str2;
    return 0;
}

Output

Allocating 51 bytes
Allocating 51 bytes
2
ZYXWVUTSRQPONMLKJIHGFEDCBA ABCDEFGHIJKLMNOPQRSTUVWXYZ

Even without using the move semantics, why there are only two heap allocations? Where does the temp string gets allocated memory? If there are two heap allocations in both the cases then what is the advantage of using std:: move() here?

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
Arun Suryan
  • 1,615
  • 1
  • 9
  • 27
  • 2
    Can you please show us the program you actually have trouble understanding ("without using the move semantics")? Also include the actual output you get from the program, and possibly the expected output. – Some programmer dude Dec 02 '20 at 18:08
  • https://stackoverflow.com/questions/10315041/meaning-of-acronym-sso-in-the-context-of-stdstring/10319672#10319672 – jtbandes Dec 02 '20 at 18:08
  • 1
    @jtbandes This one is not about SSO, don't mislead. – bloody Dec 02 '20 at 18:12
  • fwiw, this is a much simpler example of getting less allocations than there are strings https://godbolt.org/z/h6r3sr. What you mean with "Even wihtout using the move semantics" is not quite clear – 463035818_is_not_an_ai Dec 02 '20 at 18:12
  • @Someprogrammerdude Can you please now again go through the question? Or should I edit it again? – Arun Suryan Dec 02 '20 at 18:15
  • 1
    what compiler are you using ? For the first I get different output with gcc and clang (both with optimizations enabled) – 463035818_is_not_an_ai Dec 02 '20 at 18:17
  • @largest_prime_is_463035818 I am using GCC with optimizations disabled – Arun Suryan Dec 02 '20 at 18:20
  • 2
    Have you looked at the generated code to see what it does? Perhaps the compiler recognizes the "swap" pattern and generates code to swap the pointers instead? – Some programmer dude Dec 02 '20 at 18:20
  • @Someprogrammerdude What I don't understand is that why there is not any heap allocation for the string temp? How is arg1 being assigned to temp without heap allocation? – Arun Suryan Dec 02 '20 at 18:22
  • Check if your `string` implementation can share the same string (similar to `shared_ptr`). For this just add the third string `string str3(str1);`. – Alexander Dyagilev Dec 02 '20 at 18:26
  • 1
    @ArunSuryan: What version of GCC, and what command line are you using for the compile? I can't reproduce on a [simple test cases with Godbolt](https://godbolt.org/z/oG6T93); I get three allocations (though they're much smaller; 27 bytes, not 51). – ShadowRanger Dec 02 '20 at 18:43
  • There are [multiple](https://en.cppreference.com/w/cpp/memory/new/operator_new) `operator new` functions, including array versions. Perhaps one of the other functions is being called. – 1201ProgramAlarm Dec 02 '20 at 18:50
  • 1
    @Red.Wave Copy on write is not permitted since C++11. – Daniel Langr Dec 02 '20 at 18:52
  • One possible optimization is copy-on-write. You need to reimplement `operator delete` as well. U'r most probably using an outdated tool chain - in case of COW on `std::string`. – Red.Wave Dec 02 '20 at 18:58
  • @DanielLangr I see, but looks most probable. Although more aggressive automatic optimizations might be in charge. – Red.Wave Dec 02 '20 at 19:03
  • @Red.Wave It seems that you are right: https://godbolt.org/z/rP7M77. In that case, the question shouldn't be tagged with C++11. – Daniel Langr Dec 02 '20 at 19:13
  • @DanielLangr: Of course. I tested with `-std=c++17`, then noticed the tag and tried `-std=c++11`. Of course the tag would be a lie. How did they use `std::move` pre-C++11 though? – ShadowRanger Dec 02 '20 at 20:04
  • @ShadowRanger: C++11 was supposed to be C++08, and many C++23 pending features were already under the radar back then. The tool chain might've partially supported C++11. I'm not sure about the publication date of rvalue ref proposal though. – Red.Wave Dec 02 '20 at 20:58
  • @ShadowRanger for `std::move` pre C++11 @ clang there was a question recently: https://stackoverflow.com/questions/65009198/what-is-move-in-c98 – 463035818_is_not_an_ai Dec 02 '20 at 21:00

1 Answers1

1

You are likely using an implementation that behaves according to the C++98/03 standard and that implements copy-on-write for std::string.

Live demo: https://godbolt.org/z/rP7M77

If this is the case, your question should not be tagged with c++11, since copy-on-write is not permitted since then. (SSO is usually implemented instead, but this is a different story.)

If you switch to a newer GCC, you will see 3 allocations: https://godbolt.org/z/b5q1MM

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • So there will be three allocations in both the cases or only the case where I am using move semantics? – Arun Suryan Dec 03 '20 at 06:31
  • @ArunSuryan Could you please answer the question from the comments about what GCC version? Plus which compilation flags do you use? Anyway, it seems to be some partial C++11 support. COW is not allowed in C++11, and `std::move` did not exist until C++11. According to the C++ standards, you shouldn't be able to use both. – Daniel Langr Dec 03 '20 at 06:40
  • it's gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36) – Arun Suryan Dec 03 '20 at 06:42
  • @ArunSuryan Why there should be 3 allocations with move semantics? Move just "steals" resources, i.e., the stored string. Why should it allocate anything? – Daniel Langr Dec 03 '20 at 06:45
  • @ArunSuryan And yes, it seems that GCC 4.8.5 mixes COW and `std::move` features, which does not correspond with any particular C++ standard. I would suggest you not to use such an outdated version. – Daniel Langr Dec 03 '20 at 06:47