简体   繁体   English

std :: bad_function_call在visual studio中调用std :: swap时

[英]std::bad_function_call when calling std::swap in visual studio

I am trying to port my code from linux to windows. 我试图将我的代码从linux移植到Windows。 However with Visual Studio my code crashes with the following error: 但是在Visual Studio中,我的代码崩溃并出现以下错误:

Microsoft C++ exception: std::bad_function_call at memory location Microsoft C ++异常:内存位置的std :: bad_function_call

This is my code: 这是我的代码:

#include <functional>

class Foo {
public:
    Foo(int) : m_deleter{ []() {} } {}
    Foo(const Foo &) = delete;
    Foo(Foo &&) = default;
    Foo & operator=(const Foo &) = delete;
    Foo & operator=(Foo &&) = default;
    ~Foo()
    {
        m_deleter();
    }
private:
    std::function<void()> m_deleter;
};

int main() {
    Foo foo(1);
    Foo bar(2);
    std::swap(foo, bar);
}

It crashes when I use std::swap . 当我使用std::swap时它会崩溃。 In linux it worked flawlessly. 在linux中,它完美无缺。

Weirdly enough, when I try to compile it online via GCC it doesn't work either. 奇怪的是,当我尝试通过GCC 在线编译时,它也不起作用。 What am I doing wrong and why does at work at home with Clang (3.5). 我做错了什么,为什么在家里与Clang(3.5)一起工作。

EDIT: It turns out it crashes with Visual Studio 2015 and GCC 4.9.2, but not with Clang 3.5. 编辑:事实证明它与Visual Studio 2015和GCC 4.9.2崩溃,但不与Clang 3.5崩溃。

Introduction 介绍

The reason for the behavior is quite simple; 这种行为的原因很简单; m_deleter is unconditionally invoked in the destructor of Foo , even in circumstances when it isn't callable. m_deleterFoo的析构函数中被无条件地调用,即使在它不可调用的情况下也是如此。

The straight forward implementation of std::swap creates a temporary to hold the intermediate result of one of the two operands, this temporary will not have a callable m_deleter . std::swap的直接实现创建了一个临时的来保存两个操作数之一的中间结果,这个临时的将没有可调用的m_deleter


What is std::bad_function_call ? 什么是std :: bad_function_call
std::bad_function_call will be thrown if you try to call a std::function which doesn't have a valid target to invoke. 如果您尝试调用没有有效目标的std::function来调用std :: bad_function_call将被抛出。



Elaboration

We can reduce your testcase to the following, more explicit, snippet: 我们可以将您的测试用例减少到以下更明确的代码段:

 1 #include <functional>
 2 #include <utility>

 3 struct A {
 4   A () 
 5     : _cb {[]{}}
 6   { } 
 7   
 8   A (A&& src)
 9     : _cb (std::move (src._cb))
10   { } 
11   
12   A& operator= (A&& src)
13   {
14     _cb = std::move (src._cb);
15     return *this;
16   } 
17   
18 
19   ~A () {
20     _cb ();
21   } 
22   
23   std::function<void()> _cb;
24 };

25 void swap (A& lhs, A& rhs) {
26   A temporary = std::move (lhs);
27           lhs = std::move (rhs);
28           rhs = std::move (temporary);
29 } 

30 int main() {
31   A x, y;
32   swap (x, y);
33 } 

The Problem 问题

When leaving swap temporary will be destroyed, which in turn will try to invoke _cb - the problem is that temporary._cb has been moved-from on line 14 ; 当离开swap 临时将被销毁,而后者将尝试调用_cb - 问题是temporary._cb 已从14移出 ; it is no longer callable and an exception is thrown. 它不再可调用,抛出异常。


The Solution 解决方案

~A::A () {
  if (_cb) // check if callable
    _cb (); 
}

A temporary object is used in std::swap() . std::swap()使用临时对象。 When swap() returns, the temporary object's m_deleter is empty. swap()返回时,临时对象的m_deleter为空。 When the temporary destructs, the m_deleter(); 当临时析构时, m_deleter(); throws std::bad_function_call as m_deleter has no target. 抛出std::bad_function_call因为m_deleter没有目标。

The std::swap on my machine (gcc4.9.1, ubuntu) is like the following: 我机器上的std::swap (gcc4.9.1,ubuntu)如下所示:

template<typename _Tp>
  inline void
  swap(_Tp& __a, _Tp& __b)
  noexcept(__and_<is_nothrow_move_constructible<_Tp>,
           is_nothrow_move_assignable<_Tp>>::value)
  {
    _Tp __tmp = std::move(__a);
    __a = std::move(__b);
    __b = std::move(__tmp);
  }

After swap, __tmp (of type Foo ) holds a std::function<void()> object m_deleter with no target. 在swap之后, __tmp (类型为Foo )拥有一个没有目标的std::function<void()>对象m_deleter The exception is thrown when it destructs and the destructor calls m_deleter(); 析构函数和析构函数调用m_deleter();时抛出异常m_deleter();

You can reproduce your problem even with Visual C++ 2013, which does not support defaulted move constructors and assignment operators ; 即使使用Visual C ++ 2013,您也可以重现您的问题,而Visual C ++ 2013 不支持默认的移动构造函数和赋值运算符 ; the same behaviour occurs with self-written functions: 自编函数会出现相同的行为:

#include <functional>

class Foo {
public:
    Foo(int) : m_deleter{ []() {} } {}
    Foo(const Foo &) = delete;
    Foo(Foo &&src) : m_deleter(std::move(src.m_deleter)) { };
    Foo & operator=(const Foo &) = delete;
    Foo & operator=(Foo &&src) { m_deleter = std::move(src.m_deleter); return *this; }
    ~Foo()
    {
        m_deleter();
    }
private:
    std::function<void()> m_deleter;
};

int main() {
    Foo foo(1);
    Foo bar(2);
    std::swap(foo, bar);
}

You can then use the debugger in Visual Studio to verify what's going on. 然后,您可以使用Visual Studio中的调试器来验证发生了什么。 Put a breakpoint at your std::swap call. 在你的std::swap调用上放一个断点。 You will end up in the VC implementation of the function: 您将最终进入该函数的VC实现:

_Ty _Tmp = _Move(_Left);
_Left = _Move(_Right);
_Right = _Move(_Tmp);

All three of these moves will work correctly. 所有这三个动作都能正常工作。 But then the scope of the function ends, and so does the lifetime of the _Tmp variable. 但是函数的范围结束了, _Tmp变量的生命周期也结束了。 The destructor will be called on it while its m_deleter is empty, as you can see in the "Locals" section of the debugger GUI: m_deleter为空时,将在其上调用析构函数,您可以在调试器GUI的“Locals”部分中看到:

Visual Studio调试器显示<code> std :: swap </ code>调用临时变量,其析构函数将导致崩溃

Moving means that the moved-from object has to remain in a valid state for destruction, and a state which results in calling an empty std::function is not valid. 移动意味着移动的对象必须保持有效状态以进行销毁,并且导致调用空std::function状态无效。 Others have shown you the fix in the destructor already. 其他人已经向您展示了析构函数中的修复程序。

Now about this... 现在关于这个......

It turns out it crashes with Visual Studio 2015 and GCC 4.9.2, but not with Clang 3.5. 事实证明它与Visual Studio 2015和GCC 4.9.2崩溃,但不与Clang 3.5崩溃。

Both your original code and my modification crash with Clang 3.5: 你的原始代码和我的修改都崩溃了Clang 3.5:

terminate called after throwing an instance of 'std::bad_function_call'

  what():  bad_function_call

bash: line 7: 25250 Aborted                 (core dumped) ./a.out

I tried it at http://coliru.stacked-crooked.com/ , which according to clang++ --version uses clang version 3.5.0 (tags/RELEASE_350/final 217394) . 我在http://coliru.stacked-crooked.com/上尝试过,根据clang++ --version使用clang version 3.5.0 (tags/RELEASE_350/final 217394)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 导致std :: bad_function_call的原因是什么? - What causes std::bad_function_call? stst :: bad_function_call的callstack - Callstack for std::bad_function_call 意外的std :: bad_function_call递归 - unexpected std::bad_function_call in recursion std :: bad_function_call自调用递归lambda但没有提示 - std::bad_function_call of self-calling recursive lambda but no hints 将 avx 变量传递给 std::function 时引发 bad_function_call 和分段错误 - bad_function_call thrown and segmentation fault caused when passing avx variables to std::function 从外部函数构造 std::function 给出 std::bad_function_call - Constructing std::function from extern functions gives std::bad_function_call c++ std::map 比较函数导致运行时“bad_function_call” - c++ std::map compare function leads to runtime "bad_function_call" 为什么我的线程池有时会抛出 `std::bad_function_call` 或 `double free or corruption (!prev)` - Why does my thread pool sometimes throw `std::bad_function_call` or `double free or corruption (!prev)` 从不同的 cpp 调用回调函数会导致 bad_function_call - Calling Callback function from different cpp causes bad_function_call Visual Studio 2010和std :: function - Visual Studio 2010 and std::function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM