2013-11-28 107 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功能。