2

I have a piece of code, where in vector, elements are pairs of int and string. Then I want to move all elements from vector into an unordered_map<int, string>:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <unordered_map>
#include <vector>
using namespace std;

template <typename C>
void print(const C& container) {
    for (const auto& ele : container) {
        cout << "(" << ele.first << ", " << ele.second << "), ";
    }
    cout << endl;
}

int main() {
    vector<pair<int, string>> v {
        {1, "one"},
        {2, "two"},
        {3, "three"},
        {4, "four"},
        {5, "five"}
    };
    unordered_map<int, string> uMap;

    move(begin(v), end(v), inserter(uMap, begin(uMap)));

    cout << "In unordered_map:" << endl;
    print(uMap);
    cout << endl << "In vector:" << endl;
    print(v);

    return 0;
}

What I don't understand is the results:

In unordered_map:
(5, five), (4, four), (3, three), (2, two), (1, one), 
In vector:
(1, ), (2, ), (3, ), (4, ), (5, ),

Why are those integers left in vector? I thought move() function will move all the elements from vector into unordered_map, so that nothing will be left in the vector?

Heifetz_Fan
  • 439
  • 1
  • 6
  • 15

4 Answers4

3

From cppreference:

Moves the elements in the range [first, last), to another range beginning at d_first, starting from first and proceeding to last - 1. After this operation the elements in the moved-from range will still contain valid values of the appropriate type, but not necessarily the same values as before the move.

Because the algorithm is working on iterators, there is no way it could actually remove elements from the vector. They are still there and they can have the same value as before (but not necessarily).

Also the strings are still present in the vector. After moving from a std::string it is in a valid but unspecified state. In this case the strings are empty.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Thanks @idclev Then why is the string part in the pair removed from vector? – Heifetz_Fan Apr 28 '20 at 15:56
  • @dxfkgdkjfgbizldu whilst any state is allowable, there are generally at most two possibilities: either the moved-from values are unchanged, or they are put in some "empty" state. – Caleth Apr 28 '20 at 16:21
3

That's not really what "move" means here.

Yes, it's confusing.

Your new elements in the map will be move constructed from the elements in the vector. This may involve stealing resources from the elements in the vector (in particular, the dynamically-allocated string data will now belong to the new elements, and be detached from the old ones).

But that doesn't actually remove the original elements. They're still there, in a moved-from state. You'll want to clear() the vector yourself.

To quote Eljay:

std::move is in some ways an unfortunate term. But it was much shorter than std::suck_out_the_guts_and_transplant_them_into_another_object_maybe.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
1

Informally speaking, the only requirement for a moved-from object is that it can be safely destroyed later. In the case of an int, the best thing to do is to do nothing. A move does not destroy an object, nor does it remove it from a container. The objects are still in the vector, it's just that they are in a moved-from state now, which, for int, happens to be the same as before.

alain
  • 11,939
  • 2
  • 31
  • 51
  • Thanks @alain Then why is the string part in the pair removed from vector? – Heifetz_Fan Apr 28 '20 at 15:56
  • The string has an internal buffer which is moved to the other object. – alain Apr 28 '20 at 15:57
  • Can we say the move() function still has undefined behavior, as the way it handles pair seems to be unexpected, and doesn't meet the goal of destroying elements from source container thus reducing memory cost? – Heifetz_Fan Apr 28 '20 at 15:59
  • 7
    @dxfkgdkjfgbizldu • `std::move` is in some ways an unfortunate term. But it was much shorter than `std::suck_out_the_guts_and_transplant_them_into_another_object_maybe`. – Eljay Apr 28 '20 at 15:59
  • No, it's not the goal of `move` to destroy an object. The goal is to create the object in another place as cheaply as possible, the only requirement for the source object is that it can safely destroyed afterwards. – alain Apr 28 '20 at 16:03
  • 1
    @dxfkgdkjfgbizldu be careful with "undefined behavior" vs "unspecified". Sloppy speaking, "undefined" is the evil and cannot appear in a valid c++ program, "not specified" just means it could be anything. Also for `int` it is well defined what happens. For objects that cannot be moved a move is simply a copy – 463035818_is_not_an_ai Apr 28 '20 at 16:05
1

std::move doesn't actually move anything. It just performs a cast to rvalue and tells the compiler that you don't care what happens to that object anymore. In case of int, the compiler decides to copy as its the most efficient way.

You can read more about c++ move semantics here: C++ move semantics

Bugman
  • 191
  • 6