I am trying to port my code from linux to windows. However with Visual Studio my code crashes with the following error:
Microsoft C++ exception: std::bad_function_call at memory location
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
. In linux it worked flawlessly.
Weirdly enough, when I try to compile it online via GCC it doesn't work either. What am I doing wrong and why does at work at home with Clang (3.5).
EDIT: It turns out it crashes with Visual Studio 2015 and GCC 4.9.2, but not with Clang 3.5.
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.
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
.
What is 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.
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 ; 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()
. When swap()
returns, the temporary object's m_deleter
is empty. When the temporary destructs, the m_deleter();
throws std::bad_function_call
as m_deleter
has no target.
The std::swap
on my machine (gcc4.9.1, ubuntu) is like the following:
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. The exception is thrown when it destructs and the destructor calls m_deleter();
You can reproduce your problem even with Visual C++ 2013, which does not support defaulted move constructors and assignment operators ; 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. Put a breakpoint at your std::swap
call. You will end up in the VC implementation of the function:
_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. 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:
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. 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.
Both your original code and my modification crash with 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)
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.