std::function
提供了右值引用的构造函数。标准情况下,移动的功能对象会发生什么?它会是空的,以便再次调用没有效果吗?
在20.8.11.2.1p6下,function(function &&f)
使f
处于有效状态且具有未指定的值。
空状态是有效状态,因此您应该期望移出的函数对象可以为空。
因为function
执行类型擦除,并且功能对象可能会非常昂贵,所以将移出的对象留空的优化很有意义:
std::function<void()> g{std::bind{f, std::array<int, 1000>{}}};
std::function<void()> h{std::move{g}};
[通过从h
的移动构造g
之后,人们期望包含的bind
已从g
转移到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
处于非空可调用状态。
关于这个问题有太多的困惑。我将尝试清楚地布置事物...
本节描述标准定义对象的移出状态:
17.6.5.15 [lib.types.movedfrom]
C ++标准库中定义的类型的对象可以从(12.8)。移动操作可以显式指定或隐式指定产生。除非另有说明,否则这些移出的物体应置于有效但未指定的状态。
这是什么意思?这意味着,给定一个std定义的移出对象,您可以对该对象执行任何操作,而无需先验了解该对象的状态。不需要先验状态的动作是没有先决条件的动作。
例如,您可以在从clear()
移出的电话上调用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为空,则为空,否则为INVOKE的返回值(f,std :: forward(args)...,R)。
抛出:bad_function_call,如果!* this;否则,抛出任何异常通过包装的可调用对象。
请注意,没有前提条件或Requires子句。这意味着调用从function
移出的function
的调用运算符不是未定义的行为。无论function
处于什么状态,此调用都不会违反任何先决条件。
请注意,在任何情况下,规范都不会说该调用无效。因此,无效是不可能的。
该调用将调用包装的函数,或抛出bad_function_call
。这些是仅有的两个选择。它具有哪种行为取决于function
对象的状态。并且function
对象的状态未指定([lib.types.movedfrom])。
标准情况下,移动的函数对象会发生什么?
它将处于有效状态(因此可以使用该对象),但是未指定其实际状态。最后一部分意味着调用任何要求对象处于specific状态的函数不一定有效。
它将为空,以便再次调用它没有效果吗?
您无法假设会的。调用该函数要求它实际上具有要调用的函数。这是其状态的一部分。并且由于状态未指定,因此调用状态的结果也未指定。
如果要再次以某种有意义的方式使用该对象,只需创建一个新的function
并将其分配给它:
function<...> old;
function<...> new_ = std::move(old);
old = function<...>(...); //Reset to known state.
old(...); //Call is well-defined.
[func.wrap.func.con]:
function(function&& f);
template <class A> function(allocator_arg_t, const A& a, function&& f);
效果:如果!f,*这没有目标;否则,将f的目标构造为* this的目标,使f处于有效状态且未指定值。