当前位置: 首页 > 知识库问答 >
问题:

是否允许“std::function”移动其参数?

谢灵均
2023-03-14

在研究这个问题时,我注意到GCC(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的参数是由value取的),但接下来有两个移动。由于function是在cm的副本上操作的,因此它移动参数的事实可以看作是一个不重要的实现细节。但是,当将functionbind一起使用时,此行为会引起一些麻烦:

#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移动其参数?如果是这样的话,我们可以将bind返回的包装转换为带有by-value参数的std::function是否正常,即使这在处理多个占位符时触发意外行为?

共有1个答案

朱保赫
2023-03-14

指定std::function将提供的参数传递给带有std::forward的包装函数。例如,对于std::function ,函数调用运算符等效于

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

由于std::forward t不是引用类型时与std::move等效,因此这是第一个示例中的移动之一。第二个可能来自于必须通过std::function内部的间接层。

这也解释了您在使用std::bind时遇到的问题,因为包装的函数:std::bind也被指定为转发其参数,在这种情况下,它将被传递一个rvalue引用,该引用是由std::function内部的std::forward调用产生的。因此,绑定表达式的函数调用运算符将rvalue引用转发给每个参数。不幸的是,由于您重用了占位符,在这两种情况下,它都是对同一对象的rvalue引用,因此对于可移动类型,无论首先构造的哪个都将移动值,而第二个参数将得到一个空壳。

 类似资料:
  • 标准库函数bind()和function()定义于头文件<functional>中(该头文件还包括许多其他函数对象),用于处理函数及函数参数。bind()接受一个函数(或者函数对象,或者任何你可以通过”(…)”符号调用的事物),生成一个其有某一个或多个函数参数被“绑定”或重新组织的函数对象。(译注:顾名思义,bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。)例如: int

  • 来自标书§4.2.7http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#pathological-案例 它说: 这些更不可能是真正有用的代码。在此示例中,既不能转换为也不能转换为,因此这些函数甚至都不能使用普通成员语法调用。但是,您可以获取指向此类函数的指针并通过该指针调用它们。

  • 在Java 8中,Stream有一个方法reduce: 累加器运算符是否允许修改其任一参数?我不这么认为,因为JavaDoc说累加器应该是非干涉的,尽管所有示例都谈到修改集合,而不是修改集合的元素。 举个具体的例子,如果我们有 假设整数是可变的,那么可以通过将第二个参数的值添加到第一个参数中(就地)来修改第一个参数吗? 我想不会,但我也想举一个例子,说明这种干扰在哪里会引起问题。

  • 问题内容: 我拼凑了一个图片网站。基本模式是非常简单的MySQL,但是在尝试表示与图像相关联的可能的管理标志(“不合适”,“受版权保护”等)时遇到了一些麻烦。我目前的概念如下: (为了方便阅读而被截断;我发誓要搭配各种外键和索引) 在标志类型的查找表上是外键,并且您可以想象 应该 在上外键。现在的问题是,当第一次发出标志时,没有逻辑解析类型(我将其声明为的一种很好的用法)。但是,如果设置了值,则应

  • 问题内容: URI(特别是HTTP URL)是否允许包含一个或多个空格字符?如果 必须 对URL 进行编码,这是通常遵循的约定还是合法的选择? 特别是,有人可以指向RFC指出 必须 对带有空格的URL 进行编码吗? 提出问题的动机: 在对网站进行Beta测试时,我注意到某些URL的构造带有空格。Firefox似乎做对了,这让我感到惊讶!但是我希望能够将开发人员指向RFC,以便他们觉得有必要修复这些

  • 我试图检查在哪里失去了准确表示大整数的能力。所以我写了这个小片段: 这段代码似乎适用于所有编译器,除了clang。Clang生成一个简单的无限循环。戈德博尔特。 这是允许的吗?如果是,这是QoI问题吗?