[英]std::bad_function_call when calling std::swap in visual studio
我試圖將我的代碼從linux移植到Windows。 但是在Visual Studio中,我的代碼崩潰並出現以下錯誤:
Microsoft C ++異常:內存位置的std :: bad_function_call
這是我的代碼:
#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 在線編譯時,它也不起作用。 我做錯了什么,為什么在家里與Clang(3.5)一起工作。
編輯:事實證明它與Visual Studio 2015和GCC 4.9.2崩潰,但不與Clang 3.5崩潰。
這種行為的原因很簡單; m_deleter
在Foo
的析構函數中被無條件地調用,即使在它不可調用的情況下也是如此。
std::swap
的直接實現創建了一個臨時的來保存兩個操作數之一的中間結果,這個臨時的將沒有可調用的m_deleter
。
什么是std :: bad_function_call ?
如果您嘗試調用沒有有效目標的std::function
來調用std :: bad_function_call將被拋出。
我們可以將您的測試用例減少到以下更明確的代碼段:
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
臨時將被銷毀,而后者將嘗試調用_cb
- 問題是temporary._cb
已從第14行移出 ; 它不再可調用,拋出異常。
解決方案
~A::A () {
if (_cb) // check if callable
_cb ();
}
在std::swap()
使用臨時對象。 當swap()
返回時,臨時對象的m_deleter
為空。 當臨時析構時, m_deleter();
拋出std::bad_function_call
因為m_deleter
沒有目標。
我機器上的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);
}
在swap之后, __tmp
(類型為Foo
)擁有一個沒有目標的std::function<void()>
對象m_deleter
。 析構函數和析構函數調用m_deleter();
時拋出異常m_deleter();
即使使用Visual C ++ 2013,您也可以重現您的問題,而Visual C ++ 2013 不支持默認的移動構造函數和賦值運算符 ; 自編函數會出現相同的行為:
#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
狀態無效。 其他人已經向您展示了析構函數中的修復程序。
現在關於這個......
事實證明它與Visual Studio 2015和GCC 4.9.2崩潰,但不與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)
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.