jeudi 1 décembre 2016

c++ move semantics where allegedly there shouldn't be benefit

I needed to use some class today that followed this basic design:

class Task {
public:
    Task() {
        Handler::instance().add(this);
    }
    virtual void doSomething() = 0;
};

class Handler {
    std::vector<Task*> vec;
    //yea yea, we are locking the option to use default constructor etc
public:
    static Handler& instance() {
        static Handler handler;
        return handler;
    }

    void add(Task* task) {
        vec.push_back(task);
    }

    void handle() {
        for (auto t : vec) {
            t->doSomething();
        }
    }
};

template <class T, int SIZE>
class MyTask : public Task {
     T data[SIZE];
public:
    virtual void doSomething() {
        // actually do something
    }
};
//somewhere in the code:
Handler::instance().handle();

now, my class is something like

class A {
    MyTask myTask;
public:
    A(int i) {}
};

the way I wanted to do it is having a map where instances of A are values

 static std::map<int, A> map = {
     {42, A(1948)},
     {88, A(-17)}
 };

first to clarify something - this code needs to run on a real time embedded system so I'm not allowed to allocate memory using new for several legacy reasons.

My problem was that the actual objects in the map weren't the ones I explicitly created and so they didn't register in the Handler class (so I didn't get the benefit of the Handler::handle calls).

I tried figuring a nice way to solve this without doing something ugly like first creating an array of A then only point to these objects in the map for example.

I never used move semantics before but I've read little bit about them and thought they can be my solution.

however, after reading this answer (specificaly the very first example) it seemed that I can't really benefit anything from using move semantics.

I tried it anyway (coz why the heck not...) and did something like this instead:

 static std::map<int, A> map = {
     {42, std::move(A(1948))},
     {88, std::move(A(-17))}
 };

now for my surprise the copy constructor of MyTask was still called (I put print in it to verify) but for some reason now the handler registration worked well and my instances enjoyed the doSomething() calls.

I tried reading more thoroughly about std::move to understand what exactly happened there but couldn't find the answer.

can anyone explain it? does std::move moves the this pointer somehow? or maybe it just caused the registration to happen correctly somehow and had nothing real to do with the moving attempt

thanks

Aucun commentaire:

Enregistrer un commentaire