2013-11-28 105 views
8

我試圖讓下面的代碼工作:用的unique_ptr參數綁定功能爲std ::功能<void()>

#include <cstdio> 
#include <functional> 
#include <string> 
#include <memory> 

using namespace std; 

class Foo { 
public: 
    Foo(): m_str("foo") { } 

    void f1(string s1, string s2, unique_ptr<Foo> p) 
     { 
      printf("1: %s %s %s\n", s1.c_str(), s2.c_str(), p->str()); 
     } 

    void f2(string s1, string s2, Foo* p) 
     { 
      printf("2: %s %s %s\n", s1.c_str(), s2.c_str(), p->str()); 
     } 

    const char* str() const { return m_str.c_str(); } 

private: 
    string m_str; 
}; 

int main() 
{ 
    string arg1 = "arg1"; 
    string arg2 = "arg2"; 
    Foo s; 
    unique_ptr<Foo> ptr(new Foo); 


    //function<void()> f(bind(&Foo::f1, &s, arg1, arg2, std::move(ptr))); 
    function<void()> f(bind(&Foo::f2, &s, arg1, arg2, ptr.release())); 

    f(); 
} 

調用F()綁定到富:: F2(最後一個參數是原始指針)做工精細,但它綁定到富:: F1導致編譯錯誤:

test.cpp: In function ‘int main()’: 
test.cpp:36:70: error: no matching function for call to ‘std::function<void()>::function(std::_Bind_helper<false, void (Foo::*)(std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>), Foo*, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<Foo, std::default_delete<Foo> > >::type)’ 
    function<void()> f(bind(&Foo::f1, &s, arg1, arg2, std::move(ptr))); 
                    ^
test.cpp:36:70: note: candidates are: 
In file included from test.cpp:2:0: 
/usr/include/c++/4.8.2/functional:2251:2: note: template<class _Functor, class> std::function<_Res(_ArgTypes ...)>::function(_Functor) 
    function(_Functor); 
^
/usr/include/c++/4.8.2/functional:2251:2: note: template argument deduction/substitution failed: 
/usr/include/c++/4.8.2/functional:2226:7: note: std::function<_Res(_ArgTypes ...)>::function(std::function<_Res(_ArgTypes ...)>&&) [with _Res = void; _ArgTypes = {}] 
     function(function&& __x) : _Function_base() 
    ^
/usr/include/c++/4.8.2/functional:2226:7: note: no known conversion for argument 1 from ‘std::_Bind_helper<false, void (Foo::*)(std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>), Foo*, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<Foo, std::default_delete<Foo> > >::type {aka std::_Bind<std::_Mem_fn<void (Foo::*)(std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>)>(Foo*, std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>)>}’ to ‘std::function<void()>&&’ 
/usr/include/c++/4.8.2/functional:2429:5: note: std::function<_Res(_ArgTypes ...)>::function(const std::function<_Res(_ArgTypes ...)>&) [with _Res = void; _ArgTypes = {}] 
    function<_Res(_ArgTypes...)>:: 
    ^
/usr/include/c++/4.8.2/functional:2429:5: note: no known conversion for argument 1 from ‘std::_Bind_helper<false, void (Foo::*)(std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>), Foo*, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<Foo, std::default_delete<Foo> > >::type {aka std::_Bind<std::_Mem_fn<void (Foo::*)(std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>)>(Foo*, std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>)>}’ to ‘const std::function<void()>&’ 
/usr/include/c++/4.8.2/functional:2206:7: note: std::function<_Res(_ArgTypes ...)>::function(std::nullptr_t) [with _Res = void; _ArgTypes = {}; std::nullptr_t = std::nullptr_t] 
     function(nullptr_t) noexcept 
    ^
/usr/include/c++/4.8.2/functional:2206:7: note: no known conversion for argument 1 from ‘std::_Bind_helper<false, void (Foo::*)(std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>), Foo*, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<Foo, std::default_delete<Foo> > >::type {aka std::_Bind<std::_Mem_fn<void (Foo::*)(std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>)>(Foo*, std::basic_string<char>, std::basic_string<char>, std::unique_ptr<Foo>)>}’ to ‘std::nullptr_t’ 
/usr/include/c++/4.8.2/functional:2199:7: note: std::function<_Res(_ArgTypes ...)>::function() [with _Res = void; _ArgTypes = {}] 
     function() noexcept 
    ^
/usr/include/c++/4.8.2/functional:2199:7: note: candidate expects 0 arguments, 1 provided 

我在做什麼錯?

我使用gcc 4.8.2和-std = C++ 0x(-std = C++ 11也失敗了)標誌。

感謝

+1

將rval傳遞給std :: bind存在問題,請參閱http://stackoverflow.com/questions/4871273/passing-rvalues-through-stdbind f1是否真的需要獲取指針的所有權? f1可以通過const ref獲取指針,然後可以使用std :: ref將指針傳遞給std :: bind http://coliru.stacked-crooked.com/a/5ebe39ccca544bea – countfromzero

+1

請參閱http://stackoverflow.com/問題/ 9955714/do-stdbind-work-with-move-only-types-in-general-and-stdunique-ptr -in-part – zch

+0

@justinls是的,它需要擁有所有權,但是謝謝你的例子。如果我找不到使用std :: move的方法,我會使用你的建議提出一個解決方法 – rogerzanoni

回答

3

嗯它似乎真的與R值引用打交道時的std ::綁定有煩惱。一個替代方案是使用lambda函數:

function<void()> f([&]() { s.f1(arg1,arg2,std::move(ptr)); }); 

爲了這個工作,你也必須改變F1這樣的簽名,它接受的unique_ptr作爲R值參考:

void f1(string s1, string s2, unique_ptr<Foo>&& p) 

(即使的std ::綁定可以處理r值的引用,你仍然必須這樣做,因爲的std ::的unique_ptr沒有一個拷貝構造函數,只有移動的構造是可訪問的!)

不過請注意你的構造是相當危險的(如果std :: bind也行):i如果你兩次調用f(),你最終會得到一個運行時異常。

+0

請注意,在C++ 11中,您不能輕易在像這樣的lambda表達式中捕獲「unique_ptr」。如果你嘗試這個,你會在運行時得到一個空指針異常,如果你在原來的'unique_ptr'超出了範圍之後調用函數。請參閱[這個問題](http://stackoverflow.com/questions/8236521/how-to-capture-a-unique-ptr-into-a-lambda-expression)瞭解一些解決方法。 – Malvineous

8

其他答案中描述的綁定問題(截至撰寫本文時)並不是編譯器在問題中抱怨的問題。問題是std::function必須是CopyConstructible,它需要它的參數(它將被函數存儲)也是CopyConstructible。

從標準[20.9.11.2類模板函數]

template<class F> function(F f); 
template <class F, class A> function(allocator_arg_t, const A& a, F f); 

Requires: F shall be CopyConstructible. f shall be Callable (20.9.11.2) for argument types ArgTypes and return type R . The copy constructor and destructor of A shall not throw exceptions...

考慮該示例中甚至不包括在其綁定:

#include <functional> 
#include <memory> 

using namespace std; 

struct NonCopyableFunctor { 
    NonCopyableFunctor(){} 
    NonCopyableFunctor(const NonCopyableFunctor &) = delete; 
    NonCopyableFunctor(NonCopyableFunctor &&){} 
    void operator()(){} 
}; 

int main() 
{ 
    NonCopyableFunctor fun; 
    function<void()> vfun(move(fun)); // even though I move here, 
    // it still complains about a copy going on elsewhere. 
} 

下面是從鐺輸出:

[[email protected] ~]$ clang++ -std=c++11 bound_unique.cc 
In file included from bound_unique.cc:1: 
/usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2/functional:1911:10: error: call to deleted constructor of 'NonCopyableFunctor' 
      new _Functor(*__source._M_access<_Functor*>()); 
       ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
/usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2/functional:1946:8: note: in instantiation of member function 
     'std::_Function_base::_Base_manager<NonCopyableFunctor>::_M_clone' requested here 
       _M_clone(__dest, __source, _Local_storage()); 
      ^
/usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2/functional:2453:33: note: in instantiation of member function 
     'std::_Function_base::_Base_manager<NonCopyableFunctor>::_M_manager' requested here 
      _M_manager = &_My_handler::_M_manager; 
            ^
bound_unique.cc:16:20: note: in instantiation of function template specialization 'std::function<void()>::function<NonCopyableFunctor, void>' requested here 
    function<void()> vfun(move(fun)); 
       ^
bound_unique.cc:8:3: note: function has been explicitly marked deleted here 
    NonCopyableFunctor(const NonCopyableFunctor &) = delete; 
^
1 error generated. 

請注意,如果綁定unique_ptr,則生成的綁定對象將不可複製。無論如何,綁定仍然會編譯。

1

1)下面的代碼不會編譯

function<void()> f(bind(&Foo::f1, &s, arg1, arg2, std::move(ptr))); 
// just define f, not even call it 

因爲function需要調用對象是拷貝構造,但是當bind花費不可複製參數等unique_ptr返回的算符將不可複製,正如其他答案中所述。

2)所以只是不要使用functionbind。但是,下面的代碼不會因爲在步驟編譯任

auto f(bind(&Foo::f1, &s, arg1, arg2, std::move(ptr))); // a 
f();             // b 

(一)bind店你給它作爲一個左值(除reference_wrapper),並在步驟它傳送到內部仿函數(B) 。因此它要求綁定的參數是可複製的,因爲這些參數是通過值傳遞的,而不是引用。

3)然後嘗試使用原始指針。不過下面的代碼將無法編譯要麼

auto f(bind(&Foo::f1, &s, arg1, arg2, ptr.release())); 
f(); 

類似的原因一樣(2),仿函數存儲的int*,並嘗試調用時將其轉換爲參數類型unique_ptr<int>。但構造函數unique_ptr(pointer p)explicit


要編譯它,你需要這樣的

void f3(string s1, string s2, unique_ptr<Foo>& p) 
//           ^use reference; add const if you need 
{ 
    printf("3: %s %s %s\n", s1.c_str(), s2.c_str(), p->str()); 
} 

auto f(bind(&Foo::f3, &s, arg1, arg2, std::move(ptr))); 
f(); 

注意f可以多次調用,參數存儲在bind返回的對象p引用相同unique_ptr功能。