lundi 18 mai 2020

How to avoid object copy at vector list initialization and how to extend the lifetime of a temporary

Consider the code bellow where a number of objects from class "MyType" are stored inside a vector, then this vector are passed to an object of class "VectorProcessor":

// to compile, run: g++ -Wall --std=c++17 test.cpp -o test
#include <vector>
#include <iostream>

using namespace std;

class MyType {
private:
    int x;
public:
    MyType(int a_x) {
        this->x = a_x;
        cout << "MyType constructor; x=" << this->x << endl;
    }
    ~MyType() {
        cout << "MyType destructor; x=" << this->x << endl;
        this->x = 0;
    }
    MyType(const MyType &o)  {
        this->x = o.x +10;
        cout << "MyType copy constructor; x=x'+10=" << this->x << endl;
    }
    MyType& operator=(const MyType& o)  {
        this->x = o.x +20;
        cout << "MyType =copy constructor; x=x'+20=" << this->x << endl;
        return *this;
    }
    MyType(MyType &&o) {
        this->x = o.x + 30;
        cout << "MyType move constructor; x=x'+30=" << this->x << endl;
    }
    MyType & operator=(MyType &&o) {
        this->x = o.x + 40;
        cout << "MyType =move constructor; x=x'+40=" << this->x << endl;
        return *this;
    }
    friend ostream& operator<<(ostream& os, const MyType& s) {
        return os << s.x;
    }
};

template <typename T>
class VectorProcessor {
    private:
        const vector<T> &v;
    public:
        VectorProcessor(const vector<T> &a_lista): v{a_lista} {
            cout << "VectorProcessor constructor" << endl;
        }
        VectorProcessor(const vector<T> &&a_lista): v(a_lista) {
            cout << "VectorProcessor rvalue constructor" << endl;
        }
        const T& getLast() {
            return this->v[this->v.size()-1];
        }
};

int main() {
#if 1
    // test (A)
    vector<MyType> v{1,2,3,4,5};
    VectorProcessor<MyType> r(v);
#else
    // test (B)
    VectorProcessor<MyType> r(vector<MyType>{1,2,3,4,5});
#endif
    cout << "r.getLast(): " << r.getLast() << endl;

    return 0;
}

If I enable "test A", the program output is:

MyType constructor; x=1
MyType constructor; x=2
MyType constructor; x=3
MyType constructor; x=4
MyType constructor; x=5
MyType copy constructor; x=x'+10=11
MyType copy constructor; x=x'+10=12
MyType copy constructor; x=x'+10=13
MyType copy constructor; x=x'+10=14
MyType copy constructor; x=x'+10=15
MyType destructor; x=5
MyType destructor; x=4
MyType destructor; x=3
MyType destructor; x=2
MyType destructor; x=1
VectorProcessor constructor
r.getLast(): 15
MyType destructor; x=11
MyType destructor; x=12
MyType destructor; x=13
MyType destructor; x=14
MyType destructor; x=15

And If I enable "test B", the program output is:

MyType constructor; x=1
MyType constructor; x=2
MyType constructor; x=3
MyType constructor; x=4
MyType constructor; x=5
MyType copy constructor; x=x'+10=11
MyType copy constructor; x=x'+10=12
MyType copy constructor; x=x'+10=13
MyType copy constructor; x=x'+10=14
MyType copy constructor; x=x'+10=15
VectorProcessor rvalue constructor
MyType destructor; x=11
MyType destructor; x=12
MyType destructor; x=13
MyType destructor; x=14
MyType destructor; x=15
MyType destructor; x=5
MyType destructor; x=4
MyType destructor; x=3
MyType destructor; x=2
MyType destructor; x=1
r.getLast(): 0

Then I ask: Without using pointers, or smart pointers,

1) How to avoid extra copying that occours in the objects from MyType class (for both test A and B)? 2) In the "test B", why the MyType destructor are called before r.getLast()? In other words, how to extent the lifetime of the temporary vector<MyType>{1,2,3,4,5} to the end of block?

Aucun commentaire:

Enregistrer un commentaire