Is it possible to write a function move_and_clear such that, for any STL container:
do_something_with(move_and_clear(container));
is equivalent to:
do_something_with(std::move(container));
container.clear();
?
Here is my first attempt, which doesn't work. I think I got the types right (although a production version of this would probably sprinkle in some std::remove_reference's), and it compiles successfuly, but it fails or crashes because scratch is accessed after it goes out of scope.
template<class T>
T &&move_and_clear(T &t)
{
T scratch;
std::swap(scratch, t);
return std::move(scratch);
}
Here is my second attempt. This actually works, but it's a preprocessor macro, and is therefore evil:
template<class T>
T &&move_and_clear_helper(T &t, T &&scratch)
{
std::swap(scratch, t);
return std::move(scratch);
}
#define move_and_clear(t) move_and_clear_helper(t, decltype(t)())
My third attempt is another macro which also works, this time using a lambda instead of a named helper function. So it's a bit more self-contained than the previous macro, but perhaps less readable, and of course it's still evil because it's a macro:
#define move_and_clear(t) \
[](decltype(t) &tparam, decltype(t) &&scratch){ \
std::swap(scratch, tparam); \
return std::move(scratch); \
}(t, decltype(t)())
Here is a compilable program incorporating my three attempts:
/*
g++ --std=c++11 -W -Wall -g move_and_clear.cc -o move_and_clear1 -DWHICH=1
g++ --std=c++11 -W -Wall -g move_and_clear.cc -o move_and_clear2 -DWHICH=2
g++ --std=c++11 -W -Wall -g move_and_clear.cc -o move_and_clear3 -DWHICH=3
./move_and_clear1 # assert-fails
./move_and_clear2 # succeeds
./move_and_clear3 # succeeds
*/
#include <assert.h>
#include <iostream>
#include <memory>
#include <vector>
#if WHICH == 1
template<class T>
T &&move_and_clear(T &t)
{
T scratch;
std::swap(scratch, t);
return std::move(scratch);
}
#elif WHICH == 2
template<class T>
T &&move_and_clear_helper(T &t, T &&scratch)
{
std::swap(scratch, t);
return std::move(scratch);
}
#define move_and_clear(t) move_and_clear_helper(t, decltype(t)())
#elif WHICH == 3
#define move_and_clear(t) \
[](decltype(t) &tparam, decltype(t) &&scratch){ \
std::swap(scratch, tparam); \
return std::move(scratch); \
}(t, decltype(t)())
#endif
// Example "do_something_with":
// takes an rvalue reference to a vector that must have size 3,
// steals its contents, and leaves it in a valid but unspecified state.
// (Implementation detail: leaves it with 7 elements.)
template<typename T>
void plunder3_and_leave_in_unspecified_state(std::vector<T> &&v)
{
assert(v.size() == 3);
std::vector<T> pirate(7);
assert(pirate.size() == 7);
std::swap(pirate, v);
assert(pirate.size() == 3);
assert(v.size() == 7);
}
int main(int, char**)
{
{
std::cout << "Using std::move and clear ..." << std::endl << std::flush;
std::vector<std::unique_ptr<int>> v(3);
assert(v.size() == 3);
plunder3_and_leave_in_unspecified_state(std::move(v));
assert(v.size() == 7); // (uses knowledge of plunder's impl detail)
v.clear();
assert(v.empty());
std::cout << "done." << std::endl << std::flush;
}
{
std::cout << "Using move_and_clear ..." << std::endl << std::flush;
std::vector<std::unique_ptr<int>> v(3);
assert(v.size() == 3);
plunder3_and_leave_in_unspecified_state(move_and_clear(v));
assert(v.empty());
std::cout << "done." << std::endl << std::flush;
}
}
Is there a way to implement move_and_clear as a template function, without using a macro?