2013-11-28 19 views
8

Tôi đang cố gắng để làm cho công việc mã sau:chức năng Binding với đối số unique_ptr để std :: chức năng <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(); 
} 

Gọi f() ràng buộc để Foo :: f2 (tham số cuối cùng là một con trỏ thô) hoạt động tốt, nhưng ràng buộc nó với Foo :: f1 gây ra lỗi biên dịch:

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 

Tôi đang làm gì sai?

Tôi đang sử dụng gcc 4.8.2 và -std = C++ 0x (-std = C++ 11 thất bại quá) cờ.

Cảm ơn

+1

Có vấn đề với việc chuyển giá trị tới std :: bind, xem http://stackoverflow.com/questions/4871273/passing-rvalues-through-stdbind F1 có thực sự cần có quyền sở hữu con trỏ không? f1 có thể lấy con trỏ bằng const ref và sau đó bạn có thể chuyển con trỏ tới std :: bind bằng cách sử dụng std :: ref http://coliru.stacked-crooked.com/a/5ebe39ccca544bea – countfromzero

+1

Xem http://stackoverflow.com/ câu hỏi/9955714/không-stdbind-work-with-move-only-type-in-general-và-stdunique-ptr-in-part – zch

+0

@justinls Yup, nó cần có quyền sở hữu, nhưng cảm ơn ví dụ của bạn. Nếu tôi không tìm cách để làm cho nó hoạt động bằng cách sử dụng std :: move, tôi sẽ đề xuất cách giải quyết bằng cách sử dụng đề xuất của bạn – rogerzanoni

Trả lời

3

Hmm có vẻ như std :: bind có vấn đề khi xử lý các tham chiếu giá trị r. Một thay thế sẽ được sử dụng một hàm lambda:

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

Để cho tiện làm việc bạn cũng phải thay đổi chữ ký của f1 như vậy mà nó chấp nhận unique_ptr như một tài liệu tham khảo r có giá trị:

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

(Ngay cả khi std :: bind có thể xử lý tham chiếu giá trị r, bạn vẫn sẽ phải làm điều này vì std :: unique_ptr không có hàm tạo bản sao, chỉ có thể truy cập hàm khởi tạo!)

Lưu ý rằng xây dựng của bạn là khá nguy hiểm (cũng nếu std :: bind sẽ làm việc): i f bạn gọi f() hai lần, bạn sẽ kết thúc với một ngoại lệ thời gian chạy.

+0

Lưu ý rằng trong C++ 11 bạn không thể dễ dàng nắm bắt một 'unique_ptr' trong một biểu thức lambda như thế này. Nếu bạn thử điều này, bạn sẽ nhận được một ngoại lệ con trỏ null trong thời gian chạy, nếu bạn gọi hàm một khi 'unique_ptr' ban đầu đã đi ra ngoài phạm vi. Xem [câu hỏi này] (http://stackoverflow.com/questions/8236521/how-to-capture-a-unique-ptr-into-a-lambda-expression) để biết một số cách giải quyết. – Malvineous

8

Các vấn đề với liên kết được mô tả trong các câu trả lời khác (như của văn bản này) không phải là những gì trình biên dịch phàn nàn về trong câu hỏi. Vấn đề là std::function phải là CopyConstructible, yêu cầu đối số của nó (sẽ được lưu trữ bởi hàm) cũng có thể CopyConstructible.

Từ tiêu chuẩn [20.9.11.2 Lớp mẫu chức năng]

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...

Hãy xem xét ví dụ này mà thậm chí không bao gồm ràng buộc trong đó:

#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. 
} 

Dưới đây là kết quả từ kêu vang :

[[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. 

Lưu ý rằng nếu bạn liên kết một unique_ptr, đối tượng kết quả kết quả sẽ không thể sao chép được. Bind vẫn sẽ biên dịch.

1

1) Các mã sau đây sẽ không biên dịch

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

function đòi hỏi các đối tượng có thể được gọi là sao chép constructible nhưng khi bind nhận một đối số không copyable như unique_ptr các functor trở sẽ không copyable, như đã đề cập trong các câu trả lời khác.

2) Vì vậy, chỉ cần không sử dụng function cho bind.Tuy nhiên đoạn mã sau sẽ không biên dịch hoặc

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

vì ở bước (a) bind cửa hàng những gì bạn cung cấp cho nó như là một giá trị trái (trừ reference_wrapper), và vượt qua nó để functor nội bộ tại bước (b) . Do đó, nó đòi hỏi các đối số ràng buộc có thể sao chép được cho các tham số đó được truyền theo giá trị, chứ không phải các tham chiếu.

3) Sau đó thử sử dụng con trỏ thô. Tuy nhiên đoạn mã sau sẽ không biên dịch hoặc

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

Một lý do tương tự như (2), các functor lưu giữ một int*, và cố gắng chuyển nó sang tham số kiểu unique_ptr<int> khi gọi. Nhưng nhà xây dựng unique_ptr(pointer p)explicit.


Để biên dịch nó, bạn cần có một chức năng như thế này

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(); 

Lưu ý rằng f có thể được gọi nhiều lần, và các thông số p tham khảo cùng unique_ptr được lưu trữ trong đối tượng quay trở lại của bind .

Các vấn đề liên quan