2015-12-28 20 views
6

Tôi đang đọc qua "Ngôn ngữ lập trình C++ - Ấn bản lần thứ tư" và tôi đã nhập một bài tập đơn giản chỉ để nhận cú pháp C++ và vô tình tình cờ gặp một thứ khiến tôi nâng cao mày. Nói tóm lại, tôi quên để thêm () trên accept trong chính:Chức năng gọi thiếu cảnh báo danh sách đối số

bool accept() 
{ 
    cout << "Do you want to proceed (y or n)?\n"; 

    char answer = 0; 
    cin >> answer; 

    if (answer == 'y') 
    { 
     return true; 
    } 
    return false; 

} 

int main() 
{ 
    accept; 
} 

này chạy và biên dịch và tạo ra (trong VS2015) một

C4551 - chức năng gọi thiếu danh sách đối số

Tôi đã tìm thấy bản thân mình đọc và một loạt các câu hỏi trên SO nên được đóng vì chúng chủ yếu yêu cầu "gỡ lỗi mã của tôi".

Tôi nhận thấy nếu mã biên dịch và chạy, và hàm chứa câu lệnh chặn (chờ đầu vào của người dùng) và kiểu trả về, tất cả mã sẽ được thực thi như mong đợi bất kể thiếu dấu ngoặc đơn; đó không phải là trường hợp mặc dù.

Ngoài ra, tôi nhận thấy tôi sẽ thay đổi cuộc gọi thành accept trong chính, thành bool a = accept; cout << a; để thử và ngăn chặn bất kỳ tối ưu hóa nào (và điều đó thực sự xảy ra) và điều đó cũng không gọi mã số accept().

Những gì tôi đang tò mò muốn biết là:

  1. gì được cuộc gọi đến accept nhận được biên soạn vào?
  2. Tại sao mã không phải là accept được gọi là
  3. Tại sao đây chỉ là cảnh báo chứ không phải là lỗi (tôi biết tôi có thể thay đổi cấu hình để hiển thị lỗi này, tôi thắc mắc cách thức điều này được chấp nhận cú pháp theo mặc định khi kết quả thực tế khác với kết quả mong đợi vì vậy "đáng kể?" Câu hỏi này có thể là dựa trên ý kiến, bỏ qua nếu bạn đồng ý.)
  4. Chạy mã bool a = accept; cout << a; trong sản xuất chính 1 làm đầu ra. Làm thế nào điều này có thể được khi false là giá trị bool mặc định (trong C# ít nhất) và không có gì để trả về giá trị thực vì mã chấp nhận không được thực thi?
+2

có thể trùng lặp của [Tại sao không phải là C++ biên dịch phàn nàn khi tôi sử dụng các chức năng không có dấu ngoặc?] (Http://stackoverflow.com/questions/11082329/why-doesnt-the-c-compiler-complain -khi-i-sử dụng-hàm-không-dấu ngoặc đơn) và http://stackoverflow.com/questions/17073066/g-calling-function-without-parenthesis-not-f-but-f-why-always-return –

+2

@ HithamS.AlQadheeb: Khi nó ở gần, anh ta hỏi một vài thứ khác liên quan đến con trỏ hàm và phân rã thành 'bool'. Vì vậy, nó không phải là một bản sao hoàn chỉnh. –

+1

@ HithamS.AlQadheeb câu trả lời trong "trùng lặp" chỉ trả lời một phần câu hỏi của tôi. Tiêu đề của câu hỏi "trùng lặp" là hoàn toàn trái ngược với những gì tôi đang gặp, vì trình biên dịch của tôi đã đưa ra cảnh báo, vì vậy tôi sử dụng văn bản cảnh báo làm tiêu đề câu hỏi để tìm kiếm dễ dàng khi người khác gặp phải cảnh báo tương tự. Các câu hỏi khác sử dụng cùng một tiêu đề của tôi hỏi "tại sao mã của tôi không hoạt động", do đó, có thể có giá trị hơn khi đóng những câu hỏi đó lại khi đóng chữ Q của tôi khi "dupe" bạn tìm thấy (cũng không phải là dupe, và) có thể tìm kiếm được. – Kcvin

Trả lời

14
  1. Không có "gọi tới accept". Xem # 3.

  2. Vì số 1.

  3. Việc sử dụng tên hàm mà không cần Cú pháp gọi hàm (tức là: ()) có nghĩa là bạn đang truy cập vào chính hàm đó. Bạn có thể, ví dụ, lưu nó trong một con trỏ hàm (thông qua chức năng-to-con trỏ mục nát):

    using my_func = bool(*)(); //Function that takes nothing and returns a bool. 
    my_func var = accept; //Store a pointer to `accept`. 
    

    Sau đó, bạn có thể phát hành var();, mà sẽ gọi accept.

    Tuy nhiên, vì bạn không bao giờ lưu trữ hàm, trình biên dịch đoán rằng bạn có thể có nghĩa là gọi hàm, không truy cập con trỏ của hàm. Tuy nhiên, accept; là một câu lệnh C++ hợp pháp, do đó trình biên dịch không thể báo lỗi trên nó. Nó có thể phát ra một cảnh báo, vì câu lệnh hoàn thành không có gì và bạn có thể muốn gọi hàm đó. Nó không khác với tuyên bố như 1;: hoàn toàn hợp pháp, nhưng hoàn toàn vô dụng.

  4. Điều này thực hiện điều này vì thủ thuật C++. Phân tách con trỏ không null đến giá trị boolean true.Và accept phân rã thành con trỏ hàm không phải là rỗng. Do đó, khi được chuyển đổi thành bool, nó sẽ là true. Bạn vẫn không phải là gọi chức năng.

8

Trong C++, bất kỳ biểu thức nào theo sau dấu chấm phẩy đều là tuyên bố pháp lý. (Tại sao? Bởi vì C cho phép bạn làm điều này, tôi nghĩ). Điều đó có nghĩa là tất cả các điều sau đây là các tuyên bố pháp lý:

5; 
3 + 5; 
1 % 2 == 0; 

Hiệu quả của tuyên bố của biểu mẫu này là biểu thức được đánh giá và loại bỏ. Một trình biên dịch tối ưu hóa tốt sẽ loại bỏ tất cả logic ở đây, vì không cái nào trong số này có bất kỳ tác dụng phụ nào.

Trong trường hợp của bạn, viết

accept; 

là một tuyên bố pháp lý vì accept là một biểu hiện đánh giá để một tham chiếu đến các chức năng accept. Điều đó có nghĩa rằng accept; là một tuyên bố có nghĩa là "đánh giá địa chỉ của accept, sau đó loại bỏ nó." Lý do mà không có hàm nào được gọi ở đây là một tên hàm của chính nó không gọi hàm; bạn cần dấu ngoặc đơn (toán tử gọi hàm) để thực sự thực hiện cuộc gọi.Điều này rất hữu ích, ví dụ, nếu bạn muốn chuyển một hàm vào một hàm khác. Ví dụ, bạn có thể muốn vượt qua một chức năng so sánh vào std::sort, như thế này:

std::sort(range.begin(), range.end(), nameOfMyComparisonFunction) 

Ở đây, nó sẽ là một vấn đề thực sự nếu điều này đã thử gọi nameOfMyComparisonFunction, kể từ khi lập luận không thể biết cho đến khi thói quen sắp xếp bắt đầu.

Vậy tại sao đây lại là cảnh báo chứ không phải lỗi? Đó là mã C++ hoàn toàn hợp pháp, do đó trình biên dịch không thể gọi nó là lỗi. Tuy nhiên, trình biên dịch là đúng để gắn cờ nó như là một cảnh báo, vì nó gần như chắc chắn có nghĩa là bạn đã thực hiện một lỗi. Điều đó nói rằng, hầu hết các trình biên dịch có một số cài đặt báo cáo cảnh báo là lỗi và nếu bạn tăng mức cảnh báo đủ cao, trình biên dịch có thể sẽ nói "điều này quá đáng ngờ đến mức tôi giả định bạn đã làm rối tung điều gì đó".

Đối với bạn qua một - tại sao

bool a = accept; 

cuối lên thiết a-true? Trong C++, bất kỳ con trỏ null ngầm chuyển đổi thành true và bất kỳ con trỏ null ngầm chuyển đổi thành false. Trong C++, các hàm được chuyển đổi hoàn toàn thành con trỏ cho chính chúng, do đó, trong trường hợp này, accept sẽ đánh giá địa chỉ của hàm accept, không phải là rỗng, do đó, nó đặt a thành true. Khi bạn sau đó viết

cout << a << endl; 

giá trị được in như 1, bởi vì bool giá trị được in như 1 và 0 chứ không phải truefalse theo mặc định. Điều đó nói rằng, bạn có thể viết

cout << boolalpha << a << endl; 

và bạn sẽ thấy true được in thay thế.

Hy vọng điều này sẽ hữu ích!

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