簡體   English   中英

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 ++異常:內存位置的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_deleterFoo的析構函數中被無條件地調用,即使在它不可調用的情況下也是如此。

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”部分中看到:

Visual Studio調試器顯示<code> std :: swap </ code>調用臨時變量,其析構函數將導致崩潰

移動意味着移動的對象必須保持有效狀態以進行銷毀,並且導致調用空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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM