Làm cách nào để kiểm tra chính xác xem hàm std :: có trống trong C ++ 11 hay không?


96

Tôi đã tự hỏi làm thế nào để kiểm tra chính xác nếu một std::functiontrống rỗng. Hãy xem xét ví dụ này:

class Test {
    std::function<void(int a)> eventFunc;

    void registerEvent(std::function<void(int a)> e) {
        eventFunc = e;
    }

    void doSomething() {
        ...
        eventFunc(42);
    }
};

Mã này biên dịch tốt trong MSVC nhưng nếu tôi gọi doSomething()mà không khởi tạo eventFuncmã thì rõ ràng là bị treo. Đó là mong đợi nhưng tôi đã tự hỏi giá trị của là eventFuncgì? Trình gỡ lỗi nói 'empty'. Vì vậy, tôi đã sửa điều đó bằng cách sử dụng câu lệnh if đơn giản:

   void doSomething() {
        ...
        if (eventFunc) {
            eventFunc(42);
        }
   }

Điều này hoạt động nhưng tôi vẫn tự hỏi giá trị của không khởi tạo là std::functiongì? Tôi muốn viết if (eventFunc != nullptr)nhưng std::function(rõ ràng) không phải là một con trỏ.

Tại sao nếu tinh khiết hoạt động? Điều kỳ diệu đằng sau nó là gì? Và, nó là cách chính xác làm thế nào để kiểm tra nó?


8
Lưu ý rằng đó eventFunckhông phải là lambda; nó là một std::function. Bạn có thể lưu trữ lambdas bằng std::functions, nhưng chúng không giống nhau.
templatetypedef

3
Bạn nói đúng, mình đổi tiêu đề để tránh nhầm lẫn. Cảm ơn.
NightElfik

Câu trả lời:


104

Bạn đang không kiểm tra lambda trống, nhưng liệu nó std::functioncó mục tiêu có thể gọi được lưu trữ trong đó hay không. Kiểm tra được xác định rõ ràng và hoạt động vì std::function::operator boolnó cho phép chuyển đổi ngầm định sang booltrong các ngữ cảnh mà giá trị boolean được yêu cầu (chẳng hạn như biểu thức điều kiện trong một ifcâu lệnh).

Bên cạnh đó, khái niệm lambda trống không thực sự có ý nghĩa. Phía sau, trình biên dịch chuyển đổi một biểu thức lambda thành một định nghĩa struct(hoặc class), với các biến mà bạn nắm bắt được lưu trữ dưới dạng thành viên dữ liệu của nó struct. Một toán tử cuộc gọi hàm công khai cũng được định nghĩa, đó là điều cho phép bạn gọi lambda. Vậy một lambda trống sẽ là gì?


Bạn cũng có thể viết if(eventFunc != nullptr)nếu muốn, nó tương đương với mã bạn có trong câu hỏi. std::function định nghĩa operator==operator!=quá tải để so sánh với a nullptr_t.


1
Tuy nhiên, không == nullptrlàm điều tương tự? Dường như không có nghĩa vụ phải được một tình trạng quá tải cho các ==nhà khai thác gây ra một "trống rỗng" std::functionđể so sánh truevới nullptr: cplusplus.com/reference/functional/function/operators
Kyle Strand

3
@KyleStrand Có, so sánh với nullptrcũng sẽ hoạt động, if(eventFunc != nullptr)tương đương với if(eventFunc)trong câu hỏi ở trên.
Praetorian

3
Về mặt kỹ thuật, std::function::operator boolkhông cho phép chuyển đổi ngầm thành bool. Rốt cuộc, nó được đánh dấu explicit, nhưng tiêu chuẩn tạo ra một ngoại lệ cho một số cấu trúc ngôn ngữ nhất định mong đợi các biểu thức boolean, gọi nó là "được chuyển đổi theo ngữ cảnh thành bool." Bạn có thể tìm thấy đoạn mã tiêu chuẩn có liên quan và giải thích tại đây: chris-sharpe.blogspot.com/2013/07/…
bcrist

@bcrist Vâng, tôi biết rằng toán tử chuyển đổi boolean explicit, đó là lý do tại sao tôi đã cẩn thận nêu rõ cho phép chuyển đổi ngầm định thành booltrong các ngữ cảnh yêu cầu giá trị boolean . Đây chính xác là những gì đang xảy ra trong đoạn mã được đề cập.
Praetorian

5
@Praetorian Điểm tôi đang cố gắng đưa ra là tiêu chuẩn chỉ định một ý nghĩa rất cụ thể cho cụm từ "chuyển đổi ngầm định" và nó khác biệt rõ ràng với "chuyển đổi theo ngữ cảnh thành bool", cũng có một ý nghĩa rất cụ thể. Không có mối quan hệ "Là-một" ở đây. Tôi hiểu rằng người mới bắt đầu có thể không cần biết sự khác biệt giữa chuyển đổi ngầm định / rõ ràng / theo ngữ cảnh ngay lập tức, nhưng tốt hơn là bạn nên học những từ phù hợp trong tiềm thức, hơn là phải phá bỏ thói quen cũ sau này.
bcrist

21

Kiểm tra tại đây http://www.cplusplus.com/reference/filities/ Chức năng/operator_bool/

Thí dụ

// function::operator bool example
#include <iostream>     // std::cout
#include <functional>   // std::function, std::plus

int main () {
  std::function<int(int,int)> foo,bar;
  foo = std::plus<int>();

  foo.swap(bar);

  std::cout << "foo is " << (foo ? "callable" : "not callable") << ".\n";
  std::cout << "bar is " << (bar ? "callable" : "not callable") << ".\n";

  return 0;
}

Đầu ra

foo không thể gọi được.

thanh có thể gọi được.


31
Tôi nghĩ rằng câu trả lời này sẽ rõ ràng hơn nếu không có swap(). Tôi đã nghĩ rằng đầu ra đã bị ngược cho đến khi tôi nhận ra nó.
cp.engr

-1

(Hãy để tôi cung cấp một câu trả lời rõ ràng.)

Bạn có thể kiểm tra xem a std::functioncó trống không với std::function::operator bool.

true: nếu đối tượng có thể gọi được.
false: nếu không (đối tượng là một hàm rỗng)

Thí dụ

#include <iostream>
#include <functional>

int main ()
{
    std::function<int(int,int)> foo = std::plus<int>();//assigned: not empty
    std::function<int(int,int)> bar;//not assigned: empty

    std::cout << "foo is " << (foo ? "not empty" : "empty") << ".\n";
    std::cout << "bar is " << (bar ? "not empty" : "empty") << ".\n";

    return 0;
}

Đầu ra

foo không trống.
thanh trống.


2
Chuỗi kết quả của bạn được hoán đổi.
Sophit

@Sophit Bạn có chắc không? ;)
zwcloud

1
Nhận xét của bạn cho biết foo không trống và đầu ra không đồng ý. Tôi đồng ý với nhận xét của bạn.
Sophit
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.