If I have a vector class, I might have a push_back method like:
1 2 3 4 5 6 7 8 9 10 11 12
|
template <typename T>
class Vector
{
void push_back(const T & _val)
{
// Verify size of storage is sufficient here
mStorage[size++] = _val;
}
// ...impl
}
|
From the client standpoint, if an rvalue is passed in, _val will bind to the rvalue, and then will be copied via the assignment operator, so 1 copy. If you pass in a const T & or T &, same thing - 1 copy. But imagine if we wrote push back like:
1 2 3 4 5 6
|
void push_back(T _val)
{
// Verify size of storage is sufficient here
mStorage[size++] = std::move(_val);
}
|
Now, if an rvalue is passed into push_back *and* T defines a move constructor, we can add the item to mStorage with 0 copies. If a const T & or T & is passed in, we get a single copy when constructing _val. But, if T does *not* define a move constructor, the latter case will cause 2 copies (a decrease in performance).
So the question is - for a template class like the above, what's the best way to take advantage of types of T that define a move constructor? Would something like the below give best results in all cases, assuming you are dealing with objects for which deep copies are expensive? (note: if anyone can reduce the below for me, that would also be appreciated - my template meta programming skills are a bit lacking)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
template <typename T>
class Vector
{
public:
void _push_back_cref(const T & _val)
{
cout << "const ref" << endl;
new (mStorage + size++) T(_val); // Think that's the right syntax...
}
void _push_back_move(T && _val )
{
cout << "rvalue ref" << endl;
new (mStorage + size++) T(move(_val)); // Think that's the right syntax...
}
template <typename T2, bool U>
struct pusher
{
static void push_back(Vector<T2> & _vec, const T2 & _val)
{
_vec._push_back_cref(_val);
}
};
template <typename T2>
struct pusher<T2, true>
{
static void push_back(Vector<T2> & _vec, T2 _val)
{
_vec._push_back_move(move(_val));
}
};
// ...impl
};
struct Square
{
Square() { }
// Square(Square && rref) = delete; // uncomment to use _push_back_cref
};
int main()
{
Vector<Square> intVec;
Vector<Square>::pusher<Square, is_move_constructible<Square>::value>::push_back(intVec, Square());
cout << "Goodbye" << endl;
return 0;
}
|