我查看了cppreference.com,他们似乎没有noexcept
指明std::function(std::function&&)
. 这对我来说似乎很奇怪。在这种情况下,标准是否真的不提供任何保证?
4 回答
引用标准(根据您的要求):
C++11 §20.8.11.2.1/6(来自 N3290):
function(function&& f);
template <class A> function(allocator_arg_t, const A& a, function&& f);
效果:如果!f
,*this
没有目标;否则,将 的目标移动构造f
成 的目标*this
,使其f
处于具有未指定值的有效状态。
所以,对不起,没有noexcept
移动构造函数。
有一个相关的缺陷报告 2062仍然开放,但它朝另一个方向发展,可以这么说,即至少有一个noexcept
显然不应该存在,无论其理由是什么……
其目的可能是支持具有抛出移动构造函数的可调用对象。但这只是合理化方向的猜测。例如,想象在此类函数对象的向量中重新分配缓冲区,尝试移动原始对象,其中一个在中间某个地方抛出(我认为这是 Abrahams 等人的原始示例) . 哎呀,他们不能保证被移回去,我们也不能前进。所以重新分配失败,导致重新分配的操作失败,向量处于无效状态. 可调用对象的非抛出移动的要求将支持函数对象的这种一般用法,包括优化的向量重新分配(等)。恕我直言,令人怀疑其意图是否真的是进行这种权衡。
其他答案对我来说都没有真正意义。 std::function
确实有一个noexcept
默认构造函数和一个noexcept
swap
方法,所以实现者总是可以像这样定义移动构造函数:
function(function&& other) noexcept
: function()
{
swap(*this, other);
}
鉴于上述情况,我不确定为什么委员会拒绝做出行动 constructor noexcept
。在任何情况下,您都可以通过创建一个包装器来保存std::function
并使用此技术移动它来解决此问题。
我想该function
对象能够存储任意的、用户定义的、可调用的对象。当您移动function
对象时,包含的用户定义对象也会被移动,并且无法保证可以毫无例外地完成此操作。
正如其他人所说,移动std::function
构造函数不是 C++标准。noexcept
正如Peter Ruderman所说,实现者可以使用方法(这是标准)定义noexcept
移动构造函数。swap
noexcept
GCC实现实际上使用了这种技术并将std::function
移动构造函数定义为noexcept
:
/**
* @brief %Function move constructor.
* @param __x A %function object rvalue with identical call signature.
*
* The newly-created %function contains the target of @a __x
* (if it has one).
*/
function(function&& __x) noexcept : _Function_base()
{
__x.swap(*this);
}
所以如果你使用GCC,你可以假设std::function
移动构造函数是noexcept
. 但是C++标准库的其他实现可能有不同的移动构造函数实现。所以你不能依赖它。
最好有std::function
默认构造(noexcept
),然后使用swap
方法:
// the line below could throw
// std::function<void()> my_function(std::move(the_function_to_move_from));
// the code below is noexcept
std::function<void()> my_function;
my_function.swap(the_function_to_move_from);
它不像使用移动构造函数那样优雅,但至少它是可移植的。
并提防移动语义!!!它可能会做一些您不期望的事情,因为它通常被指定为使从移动的对象处于有效但未指定的状态。例如:
#include <iostream>
#include <functional>
struct A
{
void call()
{
if (fn) fn();
}
std::function<void()> fn;
};
int main()
{
std::function<void()> hello = []() { std::cerr << "Hello world" << std::endl; };
A a;
hello = std::move(a.fn);
a.call(); // may print "Hello world". Is it what you expect?
return 0;
}
GCC实现不打印" Hello world",但其他实现可能会打印。正确的方法是显式清除从移动的对象:
hello = std::move(a.fn);
a.fn = nullptr;
但这使得移动语义使用不便(IMO)。