移动后std :: function :: operator bool应该返回false吗?

时间:2015-01-21 23:25:32

标签: c++11 gcc clang move-semantics

在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在移动后失效(在布尔上下文中为假),但是当绑定参数很小(技术上,不存在)时则不然。检查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的状态是特定于类型的,但我感到惊讶这个STL类的行为不一致。这在规范中是否真的未定义?

2 个答案:

答案 0 :(得分:1)

在dyp的helpful comment看起来很震惊,!func(或者对象上的任何其他移动)之后auto foo = std::move(func)是否为真,确实是未定义的行为。 C ++ 11规范中的相关文本:

  

(6)效果:如果!f,*这没有目标;否则,移动 - 将f的目标构造到* this的目标中,使f处于具有未指定值的有效状态

未定义未指定行为的另一个胜利。

答案 1 :(得分:0)

未指定对象移动后的值。

当然,除了它是一些有效的价值之外。

有一些常见的情况:

  1. 如果复制价格低廉且不能扔,则来源不受影响。
  2. 源保持默认构建状态。
  3. 源保留目标之前的任何值。 (它被交换了。)
  4. 源处于某种残缺的状态,只能被安全地销毁和分配。