std :: function的移动构造函数的异常规范是什么?

时间:2012-11-05 23:04:10

标签: c++ c++11

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

4 个答案:

答案 0 :(得分: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

有一个相关的Defect Report 2062仍然是开放的,但是它向另一个方向发展,可以说,至少有一个noexcept显然不应该存在,无论如何理由是......

可能是意图是支持投掷移动构造函数的callables。但这只是在合理化方向上的猜测。例如,想象在这样的函数对象的向量中重新分配缓冲区,其中尝试移动原件,并且在其中一个地方抛出其中一个地方(我认为这是Abrahams et.al的原始示例) 。哦,当然,他们不能保证会被搬回来,我们也不能前进。因此,重新分配失败,导致重新分配的操作失败,向量处于无效状态。对可调用对象进行非抛掷移动的要求将支持函数对象的这种一般用法,包括优化的向量重新分配(等)。哪个恕我直言会让人怀疑这个意图真的是为了做出这种权衡。

答案 1 :(得分:4)

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

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

鉴于上述情况,我不确定委员会为何拒绝成为移动构造函数noexcept。在任何情况下,您都可以通过创建包装来保存std::function并使用此技术移动它来解决此问题。

答案 2 :(得分:3)

我认为function对象能够存储任意的,用户定义的可调用对象。移动function对象时,也会移动包含的用户定义对象,并且无法保证可以在没有异常的情况下完成此操作。

答案 3 :(得分:0)

正如其他人所说, C ++ 标准的std::function移动构造函数 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)。