是'的std :: function`允许移动它的参数?(Is `std::function

2019-06-23 09:03发布

虽然在工作这个问题 ,我注意到,海湾合作委员会(V4.7)的执行std::function时,他们采取的价值移动它的参数。 下面的代码显示了这种行为:

#include <functional>
#include <iostream>

struct CopyableMovable
{
    CopyableMovable()                        { std::cout << "default" << '\n'; }
    CopyableMovable(CopyableMovable const &) { std::cout << "copy" << '\n'; }
    CopyableMovable(CopyableMovable &&)      { std::cout << "move" << '\n'; }
};

void foo(CopyableMovable cm)
{ }

int main()
{
    typedef std::function<void(CopyableMovable)> byValue;

    byValue fooByValue = foo;

    CopyableMovable cm;
    fooByValue(cm);
}
// outputs: default copy move move

我们在这里看到的副本cm的情况下(因为这似乎是合理的byValue的参数采取值),但后来有两个动作。 由于function上的拷贝操作cm ,即其移动它的参数这一事实可以看作是一个不重要的实现细节。 但是,这种行为会导致一些麻烦, 当使用function一起bind

#include <functional>
#include <iostream>

struct MoveTracker
{
    bool hasBeenMovedFrom;

    MoveTracker()
      : hasBeenMovedFrom(false)
    {}
    MoveTracker(MoveTracker const &)
      : hasBeenMovedFrom(false)
    {}
    MoveTracker(MoveTracker && other)
      : hasBeenMovedFrom(false)
    {
        if (other.hasBeenMovedFrom)
        {
            std::cout << "already moved!" << '\n';
        }
        else
        {
            other.hasBeenMovedFrom = true;
        }
    }
};

void foo(MoveTracker, MoveTracker) {}

int main()
{
    using namespace std::placeholders;
    std::function<void(MoveTracker)> func = std::bind(foo, _1, _1);
    MoveTracker obj;
    func(obj); // prints "already moved!"
}

由标准允许这种行为? 是std::function允许移动它的参数? 如果是这样,这是正常的,我们可以通过转换返回的包装bindstd::function与价值的参数,即使使用占位符多次出现时,这会触发意外的行为?

Answer 1:

std::function被指定为所提供的参数传递到与该包装的函数std::forward 。 例如,对于std::function<void(MoveTracker)> ,函数调用操作等效于

void operator(CopyableMovable a)
{
    f(std::forward<CopyableMovable>(a));
}

由于std::forward<T>相当于std::moveT不是引用类型,这占在第一示例中的移动中的一个。 这有可能是第二个来自不必经过内部的间接层std::function

然后,这也说明了您正在与使用遇到的问题std::bind作为包装的函数: std::bind 指定转发它的参数,并且在这种情况下,它被传递从所得右值参考std::forward里面调用std::function 。 因而你的bind表达式的函数调用操作者转发一个rvalue参照每个参数。 不幸的是,因为您重复占位符,它是一个右值引用在两种情况下相同的对象,所以取其首先构建将移动值活字,第二个参数会得到一个空壳。



文章来源: Is `std::function` allowed to move its arguments?