7

我查看了cppreference.com,他们似乎没有noexcept指明std::function(std::function&&). 这对我来说似乎很奇怪。在这种情况下,标准是否真的不提供任何保证?

4

4 回答 4

5

引用标准(根据您的要求):

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 等人的原始示例) . 哎呀,他们不能保证被移回去,我们也不能前进。所以重新分配失败,导致重新分配的操作失败,向量处于无效状态. 可调用对象的非抛出移动的要求将支持函数对象的这种一般用法,包括优化的向量重新分配(等)。恕我直言,令人怀疑其意图是否真的是进行这种权衡。

于 2012-11-05T23:10:49.737 回答
4

其他答案对我来说都没有真正意义。 std::function 确实有一个noexcept默认构造函数和一个noexcept swap方法,所以实现者总是可以像这样定义移动构造函数:

function(function&& other) noexcept
  : function()
{
  swap(*this, other);
}

鉴于上述情况,我不确定为什么委员会拒绝做出行动 constructor noexcept。在任何情况下,您都可以通过创建一个包装器来保存std::function并使用此技术移动它来解决此问题。

于 2015-03-17T12:55:05.830 回答
3

我想该function对象能够存储任意的、用户定义的、可调用的对象。当您移动function对象时,包含的用户定义对象也会被移动,并且无法保证可以毫无例外地完成此操作。

于 2012-11-05T23:08:37.993 回答
0

正如其他人所说,移动std::function构造函数不是 C++标准。noexcept

正如Peter Ruderman所说,实现者可以使用方法(这是标准)定义noexcept移动构造函数。swapnoexcept

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)。

于 2018-02-22T13:09:54.887 回答