2012-03-30 34 views
7

Tôi đang cố gắng hiểu khái niệm về currying và gọi một hàm đồng bộ ba chuỗi nhưng bằng cách chỉ truyền hai chuỗi và sử dụng đối số thứ hai hai lần.Chức năng C++ liên kết các đối số lặp lại với hàm curried

Tuy nhiên, khi tôi thực hiện việc này, đối số thứ hai không được gửi đến hàm nào cả và nó in ra một chuỗi rỗng. Có một số sai lầm thực sự rõ ràng?

string concatthreestrings(string a,string b,string c){ 
    cout<<"Value of A: "<<a<<endl; 
    cout<<"Value of B: "<<b<<endl; 
    cout<<"Value of C: "<<c<<endl; 
    return a+b+c; 
} 


int main() 
{ 
    typedef std::function< string(string,string) > fun_t ; 
    using namespace std::placeholders; 
    fun_t fn = std::bind(concatthreestrings, _1, _2, _2); 
    cout<<endl<<fn("First","Second")<<endl; 

} 

Điều này đưa ra kết quả dưới đây. Không sử dụng _2 hai lần có nghĩa là đối số thứ hai được chuyển cho cả hai giây và thứ ba. Nếu sử dụng một chuỗi ở vị trí của nó làm việc tốt.

enter image description here

+0

Câu hỏi rất thú vị, tôi sẽ không mong đợi một hành vi như vậy! –

+1

Chỉ cần thêm một số sửa đổi: Đây không phải là thực sự currying. Đối số ràng buộc và currying là hai hoạt động rất giống nhau nhưng vẫn còn khác biệt, mà không nên nhầm lẫn. Currying có nghĩa là lấy một hàm nhận hàm N và biến nó thành một hàm của một đối số trả về một hàm của một đối số trả về một hàm của một đối số ... (lặp lại n Times). Bạn có thể sử dụng 'std :: bind' để thực hiện một hàm' curry' để thực hiện điều này cho bạn (với một số mở rộng). Tương tự, bạn có thể sử dụng currying để thực thi ràng buộc đối số theo cách 'std :: bind'. – LiKao

+1

@LiKao: Thật vậy, 'bind' cho phép [một phần ứng dụng] (http://en.wikipedia.org/wiki/Partial_application), không phải currying. –

Trả lời

5

Sao chép dây là tốn kém. Kể từ std::bind cho rằng giá trị của trình giữ chỗ chỉ được sử dụng một lần, nó thực hiện một số std::move trên các chuỗi. Điều này được thực hiện cho mỗi Thông số và kết quả là b hoặc c là một chuyển động, có nghĩa là chuỗi rỗng.

Bạn có thể thay đổi hành vi bằng cách dứt khoát nói những gì bạn có nghĩa là, bằng cách thông qua các đối số bằng const tham khảo:

string concatthreestrings(string const& a,string const& b,string const& c) 

Bây giờ, nó sẽ làm việc.

+2

Bạn có chắc chắn hành vi này được xác định rõ ràng không? Từ những gì tôi đọc trong tiêu chuẩn, trình bao bọc được trả về bởi 'bind' chuyển tiếp các đối số của nó tới hàm được bọc. Trong trường hợp này, theo sự hiểu biết của tôi, chúng tôi sẽ có một wrapper theo cách này (sau khi suy luận các tham số mẫu và sụp đổ): 'string g (const char (& u1) [6], const char (& u2) [7]) { concatthreestrings (forward (u1), forward (u2), forward (u2)); } '. Ba chuỗi 'a',' b' và 'c' sẽ được xây dựng từ các mảng, vì vậy tôi không thấy nơi di chuyển sẽ xảy ra. –

+0

@ipc: Có một số cách mà tôi có thể chỉ định hành vi này trong std :: bind thay vì thay đổi định nghĩa hàm chính nó – ganeshran

+0

Truy cập đối tượng đã di chuyển là UB. –

2

Tôi đã làm một vài xét nghiệm sử dụng ví dụ này nhỏ hơn thể hiện hành vi tương tự bạn có:

#include <functional> 
#include <iostream> 
#include <string> 

using std::string; 

void print(string s1, string s2) 
{ 
    std::cout << s1 << s2 << '\n'; 
} 

int main() 
{ 
    using namespace std::placeholders; 

    typedef std::function< void(string) > fn_t; 

    fn_t func = std::bind(print, _1, _1); 

    std::string foo("foo"); 
    func(foo); 
} 

// outputs: foo 

Lưu ý rằng tôi đã xác định một đối tượng chuỗi có tên là "foo" thay vì sử dụng xâu. Hành vi là như nhau, do đó, vấn đề không liên quan đến điều này.

Tôi nghĩ rằng vấn đề xuất phát từ typedef của bạn. Sự trở lại của bind (không xác định) được đúc thành một hàm lấy stringtheo giá trị, trong khi trình bao bọc được trả về bởi ràng buộc có thể lấy đối số của nó bằng cách tham chiếu rvalue và chuyển tiếp chúng hoàn toàn. Thay vì sử dụng typedef của riêng bạn, bạn nên sử dụng từ khóa auto, để loại func sẽ được trình biên dịch tự động suy ra. Nếu chúng ta thay đổi chính như sau, chúng tôi có được hành vi mong đợi:

int main() 
{ 
    using namespace std::placeholders; 

    auto func = std::bind(print, _1, _1); 

    std::string foo("foo"); 
    func(foo); 
} 

// outputs: foofoo 

giải pháp khác là để thay thế typedef của bạn để func mất thông số của nó bằng cách tham chiếu đến const:

typedef std::function< void(string const &) > fn_t; 

tôi don 't thực sự hiểu tại sao typedef khác không hoạt động ... Có lẽ chuỗi được di chuyển, như @ipc đã lưu ý, nhưng tôi không biết tại điểm nào của việc thực hiện điều này xảy ra. Tôi thậm chí không chắc chắn đây là hành vi tiêu chuẩn, vì cả hai function và wrapper trả lại bởi bind nên sử dụng chuyển tiếp hoàn hảo. Có lẽ GCC bao gồm một số tối ưu hóa di chuyển các đối số trình bao bọc khi chúng được truyền theo giá trị?

Sửa

tôi đã một số xét nghiệm, nó quay ra thi hành std::function GCC của thực hiện một động thái trên lập luận của mình, trong khi sự trở lại wrapper bởi std::bind không. Tôi vẫn không biết đây có phải là tiêu chuẩn hay không, tôi sẽ viết một câu hỏi về điều đó.

+0

ya Tôi nghĩ rằng nó là một vấn đề thực hiện trình biên dịch kể từ khi VC không triển lãm nó. – ganeshran

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