2015-03-27 24 views
12

Đáng ngạc nhiên, mã dưới đây biên dịch và chạy mà không có lỗi trên nhiều trình biên dịch và phiên bản khác nhau.Tại sao endl (std :: cout) biên dịch

#include <iostream> 

int main() { 
    endl(std::cout); 
    return 0; 
} 

Ideone link

Làm thế nào để nó biên dịch? Tôi chắc chắn không có endl trong phạm vi toàn cầu vì một mã như

std::cout << endl; 

sẽ thất bại trừ khi using được sử dụng hoặc nếu không bạn cần std::endl.

+0

Tương tự như [Hành vi thú vị của trình biên dịch với không gian tên] (http://stackoverflow.com/q/25976267/1708801) –

Trả lời

23

Hành vi này được gọi là argument dependent lookup hoặc Koenig tra cứu. Thuật toán này cho trình biên dịch không chỉ nhìn vào phạm vi cục bộ, mà còn là các không gian tên chứa kiểu của đối số trong khi tìm kiếm không đủ tiêu chuẩn gọi hàm.

Đối với ví dụ:

namespace foo { 
    struct bar{ 
    int a; 
    }; 

    void baz(struct bar) { 
    ... 
    } 
}; 

int main() { 
    foo::bar b = {42}; 
    baz(b); // Also look in foo namespace (foo::baz) 
    // because type of argument(b) is in namespace foo 
} 

Giới thiệu về đoạn mã gọi trong câu hỏi văn bản:

endl hoặc std::endl được khai báo trong std namespace as following:

template< class CharT, class Traits > 
std::basic_ostream<charT,traits>&  endl(std::basic_ostream<CharT, Traits>& os); 

hoặc

std::ostream& endl (std::ostream& os); 

cout hoặc std::coutdeclared as

extern std::ostream cout; 

Vì vậy, gọi std::endl(std::cout); là hoàn toàn tốt đẹp.

Bây giờ khi người ta chỉ endl(std::cout); gọi, bởi vì các loại lập luận cout là từ std namespace, không đủ điều kiện được cho là một chức năng endl được tìm kiếm trong std namespace và nó được tìm thấy thành công và khẳng định trở thành một chức năng và do đó một cuộc gọi đến chức năng đủ tiêu chuẩn std::endl được thực hiện.


Tiếp tục đọc:

  1. GOTW 30: Name Lookup

  2. Why does 'std::endl' require the namespace qualification when used in the statement 'std::cout << std::endl;", given argument-dependent lookup?

2

Đó là cách các thao tác dòng làm việc. Thao tác là các hàm được chuyển đến toán tử < < làm đối số. Sau đó, bên trong toán tử, chúng được gọi đơn giản.

Vì vậy, bạn có chức năng tuyên bố như

template <class charT, class traits> 
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os); 

và bạn vượt qua con trỏ của nó để điều hành < <. Và bên trong các nhà điều hành mà tuyên bố cái gì đó như

ostream& ostream::operator << (ostream& (*op)(ostream&)); 

chức năng là called.like

return (*endl)(*this); 

Do đó khi bạn nhìn thấy kỷ lục

std::cout << std::endl; 

sau đó std::endl là con trỏ hàm được truyền cho operator << làm đối số.

Trong kỷ lục

std::endl(std::cout); 

namespace prefix trước tên endl có thể được bỏ qua bởi vì trong trường hợp này, trình biên dịch sẽ sử dụng Đối số Lookup phụ thuộc. Do đó, hồ sơ này

endl(std::cout); 

sẽ biên dịch thành công.

Tuy nhiên nếu gửi kèm tên hàm trong ngoặc sau đó ADL không được sử dụng và các hồ sơ sau

(endl)(std::cout); 

sẽ không được biên dịch.

+1

Tôi nghĩ câu hỏi là tại sao 'std ::' không bắt buộc trên biểu mẫu gọi hàm –

+0

@ Mc McNabb Tôi đọc lại bài đăng và có vẻ như bạn đúng. :) –

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