【发布时间】:2015-01-21 23:25:32
【问题描述】:
在 C++11 中,std::function 是 MoveConstructible,即可以在此类对象上有意义地调用 std::move 或将它们存储在可移动类型中。一个困惑:下面的代码应该打印什么?
#include <stdio.h>
#include <functional>
#include <utility>
struct Big {
char data[1024];
};
int main(int argc, char **argv) {
Big blob;
// This bind will trigger small object optimization
std::function<void()> little = std::bind([]() { printf("little\n"); });
// This bind will not
std::function<void()> big = std::bind([](Big const& b) {
printf("big %c\n", b.data[0]);
}, blob);
auto little_moved = std::move(little);
auto big_moved = std::move(big);
// After move, one expects the source std::function to be empty
// (boolean value false)
printf("Little empty: %d\n", !little);
printf("Little (moved) empty: %d\n", !little_moved);
printf("Big empty: %d\n", !big);
printf("Big (moved) empty: %d\n", !big_moved);
return 0;
}
使用 GCC 4.8 编译,您会得到:
linux-dev:nater:/tmp$ g++-4.8 -g -o foo move_function.cc -std=c++11
linux-dev:nater:/tmp$ ./foo
Little empty: 1
Little (moved) empty: 0
Big empty: 1
Big (moved) empty: 0
对象按预期运行,使移动分配的 RHS 无效。但是,clang(Apple LLVM 6.0 版)并不是很清楚:
workbrick:nater:/tmp$ clang++ -g -o foo move_function.cc -std=c++11 -stdlib=libc++
workbrick:nater:/tmp$ ./foo
Little empty: 0
Little (moved) empty: 0
Big empty: 1
Big (moved) empty: 0
这里,当绑定参数很大时,RHS 在移动后无效(在布尔上下文中为 false),但当绑定参数很小(技术上不存在)时则不会。检查 Xcode 附带的 <functional> 的实现,我们发现行为会有所不同,具体取决于是否应用了 小对象优化:
template<class _Rp, class ..._ArgTypes>
template <class _Alloc>
function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&,
function&& __f)
{
if (__f.__f_ == 0)
__f_ = 0;
else if (__f.__f_ == (__base*)&__f.__buf_)
{
// [nater] in this optimization, __f.__f_ is not invalidate
__f_ = (__base*)&__buf_;
__f.__f_->__clone(__f_);
}
else
{
// [nater] here, the RHS gets invalidated
__f_ = __f.__f_;
__f.__f_ = 0;
}
}
现在,我知道移动分配后 RHS 的状态是特定于类型的,但我惊讶这个 Standard 类的行为不一致。这在规范中真的没有定义吗?
【问题讨论】:
-
确实如此。对于后代:(6) 效果:如果 !f,*this 没有目标;否则,将 f 的目标移动构造成 *this 的目标,使 f 处于未指定值的有效状态。 随意将其提升为答案,我会接受它。
标签: c++ c++11 gcc clang move-semantics