1

Consider the following code:

#include <iostream>
#include<vector>
#include<unordered_map>
class Domain {
    public:    
        enum class fieldname {
            pos_x, pos_y
        };
        std::unordered_map<fieldname, std::vector<double>> data;
};
int main()
{
    Domain d;
    std::vector<double> f = {1, 3, 4, 5};
    d.data[Domain::fieldname::pos_x] = f;  
    // if f is large? 
    // d.data[Domain::fieldname::pos_x] = std::move(f);   
    // use d for other stuff 
    // ...
    return 0;
}

I have assigned f to the map member using a copy. I would like to find out if using std::move like in the commented line below it will be different in terms of a) memory usage b) runtime when f is a large vector (say 10% RAM). Thank you.

P. Nair
  • 79
  • 7
  • related/dupe: https://stackoverflow.com/questions/36827900/what-makes-moving-objects-faster-than-copying – NathanOliver Apr 18 '22 at 13:19
  • 3
    The question in the title and the question in the body are two different questions entirely. – Taekahn Apr 18 '22 at 13:21
  • This calls the copy assignment operator; you can check this by replacing the use of `std::vector` with a custom class printing output on the use of copy/move assignment. `f` is not implicitly convertible to an rvalue reference which is the reason for the copy assignment operator being a valid choice here, but not the move assignment operator. – fabian Apr 18 '22 at 13:22
  • _"Does std::vector "move" implicitly?"_ - It moves if you assign an _rvalue_ (a _prvalue_ or an _xvalue_) to it. With `std::move(f)` you get an _xvalue_ and the move assignment operator will be used. Without `std::move` it will use the copy assignment operator. – Ted Lyngmo Apr 18 '22 at 13:26
  • You can observe the difference by checking `f.size()` before and after. When copied, the size will remain the same. When moved from, `f` will become empty afterwards; the memory it used to manage has been transferred to another vector. – Igor Tandetnik Apr 18 '22 at 13:31
  • @IgorTandetnik: An object that's been moved from has undefined state. I'm sure many implementations do leave a moved-from vector with a size of 0, but I don't think you can rely on that. The only safe thing you can do with a moved-from object is to let it fall out of scope so that its constructor will do any necessary cleanup. – Adrian McCarthy Apr 18 '22 at 13:38
  • @AdrianMcCarthy Technically all library types are left in an unspecified but valid state. This generally means all functions that do not have preconditions are valid to use. – NathanOliver Apr 18 '22 at 13:42
  • @AdrianMcCarthy While this is true in general, the move constructor for `std::vector` specifically guarantees that the moved-from vector is left empty. – Igor Tandetnik Apr 18 '22 at 13:43
  • @IgorTandetnik `d.data[Domain::fieldname::pos_x] = std::move(f);` is a move assignment to a default constructed instance, there is no move constructor involved. The result of the following `size()` would be unspecified. – François Andrieux Apr 18 '22 at 13:47
  • @FrançoisAndrieux Right. I stand corrected. Move-constructor guarantees the moved-from vector is empty, but move-assignment does not. – Igor Tandetnik Apr 18 '22 at 13:49
  • @IgorTandetnik: Thanks. I didn't know `std::vector`'s move constructor made that guarantee. Looking at cppreference.com, I don't see the same promise made for the move assignment operator, which @FrancoisAndrieux pointed out is the one in play here. – Adrian McCarthy Apr 18 '22 at 13:53

1 Answers1

2

First, let's address the "implicit move" thing.

In this case, f is an lvalue. It cannot be bound to rvalue reference. This means the std::vector type cannot use the move constructor and will fall back to the copy constructor.

On the other hand, this code will move:

int main()
{
    Domain d;
    d.data[Domain::fieldname::pos_x] = std::vector<double>{1, 3, 4, 5};  
    return 0;
}

In this case, the entity std::vector<double>{1, 3, 4, 5} is a prvalue and will be moved from.

If the vector is really large and new memory cannot be allocated, you'll get a std::bad_alloc exception, not a move.


Now for the resource use difference.

When you copy a vector, a second memory allocation will be requested to the memory allocator. Even if the original vector is deallocated briefly after, you had a moment in time where you needed twice the amount of memory to copy the vector.

As for the speed, generally we can assume memory allocations and copying is slower, but you can't be really sure as there is always special cases and details that weren't thought of. If there is a need for performance, you must measure the code, and optimize it to fit the performance needs. Without measurements, your changes could makes it slower without you knowing it.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141