Hỏi về std::function và Lambda

struct MyValue {
  int value;
  int fifth() {return value/5;}
};
  std::function<int(MyValue&)> value = &MyValue::value;  // pointer to data member
  std::function<int(MyValue&)> fifth = &MyValue::fifth;  // pointer to member function

  MyValue sixty {60};

  std::cout << "value(sixty): " << value(sixty) << '\n';
  std::cout << "fifth(sixty): " << fifth(sixty) << '\n';

Cho e hỏi vì sao con trỏ hàm lại có thể trỏ vào data member được vậy ạ?

std::function<void()> f() 
{ std::string h("Hello WOrld"); 
  return [=] { std::cout<< h << std::endl; }; 
}

Cách ghi f() như trên nghĩa là sao vậy ạ?
E cảm ơn!

1 Like

Cái này chỉ là cú pháp thôi bạn :smiley: chứ không trỏ vào được đâu.

Nó sẽ từa tựa như này:

int value(MyValue& myValue) {
   return myValue.value;
}
5 Likes

https://en.cppreference.com/w/cpp/utility/functional/function em vào đây mà ngâm cứu :v

Nhớ ko lầm thì hình như mấy ông c++ committee có tính chuyện cho c++ tính năng universal function gì đó, nghĩa là viết obj.func() thì sẽ viết được lại thành func(obj) và ngược lại. Chiều ngược lại rất hay ở chỗ muốn extend bất kì class nào sẽ trở nên rất dễ dàng, ví dụ muốn thêm hàm convert chuỗi thành in hoa thì chỉ cần viết một free function std::string& toupper(std::string&) là xong, gọi str.toupper() thoải mái :V Đáng tiếc là không được chấp nhận. Thôi thì với std::function ta có thể làm chiều member function thành free function vậy :V Ko có cũng làm được, có thì viết dễ hơn thôi :V Mà std::function hình như bản chất nó là một con trỏ đa hình nên xài thông qua nó sẽ chậm hơn xài trực tiếp hoặc viết hẳn một free function ra.

5 Likes

Còn lambda chỉ là anonymous function object hay gọi tắt là anonymous functor thôi :V Mấy ông coder lười đặt tên quá nên đẻ ra cái lambda cho tiện :V Trong c++ có quá tải dc toán tử () nên 1 obj có thể behave như 1 function: obj() chả khác gì func(), nhưng functor hơn free function ở chỗ nó có thêm state riêng của nó, vd obj có thể có thêm obj.value1, obj.value2. Lambda capture mấy biến thì cũng như ta khai báo thêm data member cho functor vậy :V

Để capture this, ta capture thông qua copy [=] chứ ko phải reference. Khi capture con trỏ đặc biệt this thì ta cũng xài mấy member function của nó thắng luôn mà ko cần thêm this->

Còn gán lambda đc cho std::function thì vì std::function nó là 1 con trỏ đa hình nó thích trỏ tới đâu thì nó trỏ thôi :V Cái giá phải trả cho sự tiện lợi này là 1 pointer dereference.

4 Likes

Hi UltraViolet.
Theo mình thì cần tìm hiểu một chút về con trỏ hàm trong C (một ngôn ngữ trong sáng và đơn giản). Trong C bạn có thể truy cập đến mọi vùng nhớ. Với vùng nhớ biên thì có thể đọc ghi giá trị của nó. Vậy với vùng nhớ mã thực thi thì sao ? Tất nhiên là thực thi nó (Bạn vẫn thấy lỗi chàn bộ đệm cho phép thực thi các lệnh không được phép đó là khi vùng nhớ thực thi bị ghi dè và các hàm bị thay đổi nội dung). Hiểu đơn giản con trỏ hàm là một biến và bạn có thể gán giá trị cho nó và thực thi đoạn code mà nó trỏ đến. Vậy nó có tác dụng gì ? Khi lập trình các sự kiện nó cho phép linh hoạt trong chuyển hướng thực thi của trương trình. VD:

processing(x)
    if state = STOP:
        function_stop(x)
    elif state = START:
        function_start(x)

Mọi ghứ đều tốt tuy nhiên khi bạn thêm nhiều trạng thái hơn thì cần phải sửa lại các đoạn code cũ và điều đó không phải là cách làm hay.

class Base // Lớp cơ sở xử lý.
    set_function(function)
       this.function = function
    processing(x)
       this.function(x)

class Contron // Lớp điều khiển luồng chương trình.
    set_state(state)
        if state = STOP:
            Base.set_function(function_stop)
            return True
        elif state = START:
            Base.set_function(function_start)
            return True
        return False

class SubContron : Contron // Lớp mở rộng mới sau một vài năm
    set_state(state)
        if Contron.set_state(state)
            return True
        elif state = RESUME:
            Contron.Base.set_function(function_resume)
            return True
        return Flase

Bạn sẽ nhận ra nó khi nhấn vào một nút ấn trên giao diện. Mọi nút bấm đều như nhau chỉ là các sự kiến click được gán khác nhau.

5 Likes

Câu hỏi đặt ra là những thứ nguy hiển trên liên quan gì đến std::function và Lambda ? Vì C++ xây dựng theo góc nhìn hướng đối tượng nên sẽ có vấn đề với this (vì con trỏ hàm chỉ là gọi hàm với đầu vào và đầu ra mà không có khái niệm rằng phương thức đó gắn với đối tượng nào this) bạn có thể xem trong ví // store a call to a member function trong link tntxtnt. Vậy còn Lambada ?. Khi dùng std::function bạn sẽ thấy xuất hiện các hàm bạn định nghĩa nhưng không dùng bao giờ VD: function_resume trong ví dụ trên (SubContron là người định nghĩa nó nhưng nó không dùng bao giờ mà thay vào đó là Base sẽ dùng nó). Vậy nên để tường minh và ngắn gọn người ta đưa ra Lambda. Chú ý nó khác với việc các hàm được dùng chỉ một lần với mục đích chia nhỏ hàm ra để dễ quản lý. VD:

state_init()
state_run()
state_destroy()

call()
    state_init()
    state_run()
    state_destroy()
5 Likes

Trong C# có cài này có lẽ vì triết lý thiết kế ngắn gọn của của nó. Mục đích cho các hàm kiểu như static (ToString(obj) gắn với một loại đối tượng và có thể gọi kể cả khi obj = null). Khi đó có thể gọi obj.ToString() thay vì gọi Obj::ToString(obj).

4 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?