ELEC
ELEC

Reputation: 265

Taking advantage of move semantics in C++

I have a problem where I want to store vectors of structs inside individual container class.

The problem is that while creating this container for these vectors, I waste resources because the vectors are copied; not moved. Why are the resources in the temporary returned by create() not transferred to local variable master_data in the main function?

Sample code that I assumed would work is presented below (and in coliru HERE):

#include <stdio.h>
#include <vector>
#include <stdlib.h>

// Specific data struct
typedef struct DataA {
    DataA(): a(rand()), 
             b(rand()) {}
    int a,b;    
} DataA;

// Another specific data struct
typedef struct DataB {
    DataB(): c(rand()), 
             d(rand()) {}
    int c,d;
} DataB;

// Container of all relevant data
typedef struct Master {
    Master(const std::vector<DataA> &a, const std::vector<DataB> &b) : data_a(std::move(a)), data_b(std::move(b)) {}
    Master(const Master &rhs) : data_a(std::move(rhs.data_a)), data_b(std::move(rhs.data_b)) {}

    std::vector<DataA> data_a;
    std::vector<DataB> data_b;
} Master;


Master create() {
    std::vector<DataA> data_a(10);
    std::vector<DataB> data_b(10);

    printf("data_a address inside create()   : %p\n", (void*)data_a.data());
    printf("data_b address inside create()   : %p\n", (void*)data_b.data());

    return {data_a, data_b};
}


int main()
{  
    Master master_data(create());

    printf("data_a address outside create()  : %p\n", (void*)master_data.data_a.data());
    printf("data_b address outside create()  : %p\n", (void*)master_data.data_b.data());
    return 0;
}

Upvotes: 1

Views: 103

Answers (2)

gsamaras
gsamaras

Reputation: 73366

Here:

Master(const Master &rhs) : data_a(std::move(rhs.data_a)), data_b(std::move(rhs.data_b)) {}

the parameters are constant, which prevent them from being moved, since they are immutable.

Try using something like this instead:

Master(Master&& rhs) : data_a(std::move(rhs.data_a)), data_b(std::move(rhs.data_b)) {}

where I dropped const and enhanced your parameter with &&.

Similarly for the constructor, you would do:

Master(std::vector<DataA>&& a, std::vector<DataB>&& b) : data_a(std::move(a)), data_b(std::move(b)) {}

Last, but not least, you should modify your create function like so (otherwise you will get a compilation error about not being able to bind):

Master create() {
    ...
    return {std::move(data_a), std::move(data_b)};
}

Upvotes: 1

Akira
Akira

Reputation: 4473

Your constructor:

Master(const std::vector<DataA> &a, const std::vector<DataB> &b);

accepts the a and the b parameters as constant references. You have to define it to accept rvalue references as following:

Master(std::vector<DataA>&& a, std::vector<DataB>&& b);

But in this case you have to indicate that data_a and data_b inside the create() function may be moved:

Master create() {
    std::vector<DataA> data_a(10);
    std::vector<DataB> data_b(10);

    printf("data_a address inside create()   : %p\n", (void*)data_a.data());
    printf("data_b address inside create()   : %p\n", (void*)data_b.data());

    return { std::move(data_a), std::move(data_b) };
}

Upvotes: 0

Related Questions