Reputation: 1902
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<bool, 128> 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
edit:
to further clarify what I'm asking:
I understand the use of std::move was not contributing to what's being done there.
But for some reason it did get my objects in the map to get the doSomething() calls through the handler. I'm looking for that reason
on a side note as it probably belongs to a different question - is there any decent way to initialize a map this way without the overhead of creating each object twice?
Upvotes: 2
Views: 75
Reputation: 36802
Your question has a lot more in it than it needs to but I think I understand the root question here. The std::map
constructor receives an initialization_list
, you're calling (5) from this list. Objects are copied out of an initializer_list
when iterating over it rather than moved because a copy of an initializer_list
doesn't copy the underlying objects. The same affects other std
containers, here is an example with vector
to demonstrate. (live link)
#include <vector>
#include <iostream>
struct Printer {
Printer() { std::cout << "default ctor\n"; }
Printer(const Printer&) { std::cout << "copy\n"; }
Printer(Printer&&) { std::cout << "move\n"; }
};
int main() {
std::vector<Printer> v = {Printer{}};
}
if you use {std::move(Printer{})}
you'll add another move into the mix that the compiler can't easily get optimize away.
Upvotes: 1