【发布时间】:2016-10-27 00:02:16
【问题描述】:
是否可以编写一个函数 move_and_clear 这样, 对于任何 STL 容器:
do_something_with(move_and_clear(container));
相当于:
do_something_with(std::move(container));
container.clear();
?
这是我的第一次尝试,但不起作用。 我想我的类型是正确的 (虽然这个的生产版本可能会洒在一些 std::remove_reference's),它编译成功, 但它失败或崩溃,因为在它超出范围后访问了临时。
template<class T>
T &&move_and_clear(T &t)
{
T scratch;
std::swap(scratch, t);
return std::move(scratch);
}
这是我的第二次尝试。这确实有效,但它是一个预处理器宏,因此是邪恶的:
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)())
我的第三次尝试是另一个同样有效的宏,这次 使用 lambda 而不是命名的辅助函数。 所以它比之前的宏更独立一些, 但也许可读性较差,当然它仍然是邪恶的,因为它是一个宏:
#define move_and_clear(t) \
[](decltype(t) &tparam, decltype(t) &&scratch){ \
std::swap(scratch, tparam); \
return std::move(scratch); \
}(t, decltype(t)())
这是一个包含我的三个尝试的可编译程序:
/*
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;
}
}
有没有办法在不使用宏的情况下将 move_and_clear 实现为模板函数?
【问题讨论】:
-
“清晰”是什么意思? -除非,你还想清除容器使用的底层内存分配,为什么不简单地使用
clear()成员函数呢? - 顺便说一句,std::move已经窃取了内容 -
@WhiZTiM 在容器的 clear() 成员函数的意义上“清除”,正如我在开头对问题的明确描述中所说的那样。因为,例如,我有在一百个不同的地方调用 std::move 后跟 clear() 的代码,而且很容易忘记或放错 clear(),所以我想将它们组合成一个调用,如果可能。
-
从
do_something_with()调用clear()不是更好吗? -
@el.pescado 也许,但我没有写 do_something_with() 所以这不是一个选项。例如,说 do_something_with() 是一个移动构造函数,它使源处于“有效但未指定的状态”。
-
@DonHatch 在标准容器中,只有
std::string在移出时可能不清楚(当 SBO 发生时),即使在那里,我怀疑每个实现都会做一点工作来设置size 为 0。在实践中,实际移动(不是调用std::move,而是移动并消耗结果)将清除。你只是偏执,还是有一个实际的例子,std::move没有做你要求发生的事情?
标签: c++ c++11 move-semantics rvalue-reference