std::bad_function_call 在 visual studio 中调用 std::swap 时
std::bad_function_call when calling std::swap in visual studio
我正在尝试将我的代码从 linux 移植到 windows。但是 Visual Studio 我的代码崩溃并出现以下错误:
Microsoft C++ exception: std::bad_function_call at memory location
这是我的代码:
#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);
}
当我使用 std::swap
时它崩溃了。在 linux 中,它完美地工作。
奇怪的是,当我尝试通过 GCC compile it online 时它也不起作用。我做错了什么,为什么在家里用 Clang (3.5) 工作。
编辑:事实证明它在 Visual Studio 2015 和 GCC 4.9.2 中崩溃,但在 Clang 3.5 中没有。
std::swap()
中使用了一个临时对象。当swap()
returns时,临时对象的m_deleter
为空。当临时破坏时,m_deleter();
抛出 std::bad_function_call
因为 m_deleter
没有目标。
我的机器(gcc4.9.1,ubuntu)上的std::swap
是这样的:
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);
}
交换后,__tmp
(Foo
类型)持有一个没有目标的 std::function<void()>
对象 m_deleter
。析构时抛出异常,析构函数调用m_deleter();
简介
该行为的原因很简单; m_deleter
在 Foo
的析构函数中被无条件调用,即使在它不可调用的情况下也是如此。
std::swap
的直接实现创建了一个临时文件来保存两个操作数之一的中间结果,这个临时文件不会有可调用的 m_deleter
。
什么是std::bad_function_call?
std::bad_function_call will be thrown if you try to call a std::function
没有可调用的有效目标。
详细说明
我们可以将您的 testcase 简化为以下更明确的代码片段:
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 }
问题
当离开 swap
时 temporary 将被销毁,这反过来会尝试调用 _cb
- 问题是 temporary._cb
有在 14 行从 移出;它不再可调用并抛出异常。
解决方案
~A::A () {
if (_cb) // check if callable
_cb ();
}
即使使用 Visual C++ 2013 也可以重现您的问题,not support defaulted move constructors and assignment operators;自写函数也会出现相同的行为:
#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);
}
然后您可以使用 Visual Studio 中的调试器来验证发生了什么。在您的 std::swap
调用处放置一个断点。您将最终进入函数的 VC 实现:
_Ty _Tmp = _Move(_Left);
_Left = _Move(_Right);
_Right = _Move(_Tmp);
所有这三个动作都可以正常工作。但是函数的作用域结束了,_Tmp
变量的生命周期也结束了。析构函数将在其 m_deleter
为空时对其调用,如您在调试器 GUI 的 "Locals" 部分中所见:
移动意味着被移动的对象必须保持有效状态才能销毁,导致调用空 std::function
的状态无效。其他人已经向您展示了析构函数中的修复。
现在关于这个...
It turns out it crashes with Visual Studio 2015 and GCC 4.9.2, but not
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
我在 http://coliru.stacked-crooked.com/ 尝试过,根据 clang++ --version
使用 clang version 3.5.0 (tags/RELEASE_350/final 217394)
。
我正在尝试将我的代码从 linux 移植到 windows。但是 Visual Studio 我的代码崩溃并出现以下错误:
Microsoft C++ exception: std::bad_function_call at memory location
这是我的代码:
#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);
}
当我使用 std::swap
时它崩溃了。在 linux 中,它完美地工作。
奇怪的是,当我尝试通过 GCC compile it online 时它也不起作用。我做错了什么,为什么在家里用 Clang (3.5) 工作。
编辑:事实证明它在 Visual Studio 2015 和 GCC 4.9.2 中崩溃,但在 Clang 3.5 中没有。
std::swap()
中使用了一个临时对象。当swap()
returns时,临时对象的m_deleter
为空。当临时破坏时,m_deleter();
抛出 std::bad_function_call
因为 m_deleter
没有目标。
我的机器(gcc4.9.1,ubuntu)上的std::swap
是这样的:
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);
}
交换后,__tmp
(Foo
类型)持有一个没有目标的 std::function<void()>
对象 m_deleter
。析构时抛出异常,析构函数调用m_deleter();
简介
该行为的原因很简单; m_deleter
在 Foo
的析构函数中被无条件调用,即使在它不可调用的情况下也是如此。
std::swap
的直接实现创建了一个临时文件来保存两个操作数之一的中间结果,这个临时文件不会有可调用的 m_deleter
。
什么是std::bad_function_call?
std::bad_function_call will be thrown if you try to call a std::function
没有可调用的有效目标。
详细说明
我们可以将您的 testcase 简化为以下更明确的代码片段:
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 }
问题
当离开 swap
时 temporary 将被销毁,这反过来会尝试调用 _cb
- 问题是 temporary._cb
有在 14 行从 移出;它不再可调用并抛出异常。
解决方案
~A::A () {
if (_cb) // check if callable
_cb ();
}
即使使用 Visual C++ 2013 也可以重现您的问题,not support defaulted move constructors and assignment operators;自写函数也会出现相同的行为:
#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);
}
然后您可以使用 Visual Studio 中的调试器来验证发生了什么。在您的 std::swap
调用处放置一个断点。您将最终进入函数的 VC 实现:
_Ty _Tmp = _Move(_Left);
_Left = _Move(_Right);
_Right = _Move(_Tmp);
所有这三个动作都可以正常工作。但是函数的作用域结束了,_Tmp
变量的生命周期也结束了。析构函数将在其 m_deleter
为空时对其调用,如您在调试器 GUI 的 "Locals" 部分中所见:
移动意味着被移动的对象必须保持有效状态才能销毁,导致调用空 std::function
的状态无效。其他人已经向您展示了析构函数中的修复。
现在关于这个...
It turns out it crashes with Visual Studio 2015 and GCC 4.9.2, but not 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
我在 http://coliru.stacked-crooked.com/ 尝试过,根据 clang++ --version
使用 clang version 3.5.0 (tags/RELEASE_350/final 217394)
。