23

std::function从右值 ref 提供构造函数。按照标准,移动的函数对象会发生什么?它会是空的,因此再次调用它没有效果吗?

4

4 回答 4

25

围绕这个问题有太多的困惑。我会尽量把事情说清楚...

本节描述标准定义对象的移出状态:

17.6.5.15 [lib.types.movedfrom]

C++ 标准库中定义的类型的对象可以从 (12.8) 中移出。移动操作可以显式指定或隐式生成。除非另有说明,否则此类移出的对象应置于有效但未指定的状态。

这是什么意思?这意味着给定一个标准定义的移动对象,您可以对该对象执行任何操作,而无需先验地了解该对象的状态。不需要对当前状态的先验知识的动作类别是那些没有先决条件的动作。

例如,您可以调用clear()move-from,vector因为在 上没有先决条件vector::clear()。但是你不能打电话pop_back(),因为那确实有先决条件。

特别看一下以下的呼叫操作员function

20.8.11.2.4 [func.wrap.func.inv]

R operator()(ArgTypes... args) const

效果:INVOKE(f, std::forward(args)..., R) (20.8.2),其中 f 是 *this 的目标对象 (20.8.1)。

返回:如果 R 为 void,则返回无,否则返回 INVOKE (f, std::forward(args)..., R) 的值。

抛出:bad_function_call if !*this; 否则,包装的可调用对象引发的任何异常。

请注意,没有前置条件或 Requires 子句。这意味着调用function移动源的调用运算符function不是未定义的行为。无论处于何种状态function,您都不会在此调用中违反任何先决条件。

请注意,在任何情况下,规范都没有说调用无效。所以没有效果是不可能的。

该调用将调用包装函数,或抛出一个bad_function_call. 这是仅有的两个选择。它的行为取决于function对象的状态。并且function对象的状态是未指定的([lib.types.movedfrom])。

于 2012-12-03T15:36:10.657 回答
21

function(function &&f)在20.8.11.2.1p6 下,f处于未指定值的有效状态

空状态是有效状态,因此您应该期望移出的函数对象可以为空。

因为function执行类型擦除,并且函数对象可能非常昂贵,所以将移动对象留空的优化是有意义的:

std::function<void()> g{std::bind{f, std::array<int, 1000>{}}};
std::function<void()> h{std::move{g}};

h通过 move from 构建之后g,人们会期望包含的内容bind已经从gto转移h而不是复制,因此g将留空。

对于以下程序,gcc 4.5.1 打印empty

#include <functional>
#include <iostream>
void f() {}
int main() {
    std::function<void()> g{f}, h{std::move(g)};
    std::cout << (g ? "not empty\n" : "empty\n");
}

这不一定是最佳行为;内联小型可调用对象(例如函数指针)会造成复制可调用对象比移动它并清空移出对象更有效的情况,因此另一个实现可能会g处于非空可调用状态。

于 2012-12-03T09:48:01.347 回答
7

按照标准,移动的函数对象会发生什么?

它将处于有效状态(因此可以使用该对象),但它所处的实际状态是未指定的。最后一部分意味着调用任何需要对象处于特定状态的函数不一定会起作用。

它会是空的,因此再次调用它没有效果吗?

不能假设它会。调用该函数需要它实际上有一个要调用的函数。这是其状态的一部分。并且由于状态是未指定的,调用它的结果是未指定的。

如果您想再次以某种有意义的方式使用该对象,只需创建一个新对象function并将其分配给它:

function<...> old;
function<...> new_ = std::move(old);
old = function<...>(...); //Reset to known state.
old(...); //Call is well-defined.
于 2012-12-03T09:55:32.517 回答
0

[func.wrap.func.con]:

function(function&& f);
template <class A> function(allocator_arg_t, const A& a, function&& f);

效果:如果 !f,*this 没有目标;否则,将 f 的目标移动构造成 *this 的目标,使 f 处于未指定值的有效状态。

于 2012-12-03T09:49:58.393 回答