Có phải Bjarne sai về ví dụ này của ADL, hay tôi có lỗi trình biên dịch?


81

Tôi đang đọc Ngôn ngữ lập trình C ++, Ấn bản lần thứ 4 (bởi Bjarne Stroustrup ) về. Đây là câu trích dẫn (26.3.6, ADL trung bình):

Tra cứu phụ thuộc vào đối số (thường được gọi là ADL) rất hữu ích để tránh dài dòng (14.2.4). Ví dụ:

#include <iostream>

int main()
{
    std::cout << "Hello, world" << endl; // OK because of ADL
}

Nếu không có tra cứu phụ thuộc vào đối số, người endlthao tác sẽ không được tìm thấy. Như vậy, trình biên dịch nhận thấy rằng đối số đầu tiên <<là một ostreamđược định nghĩa trong std. Do đó, nó sẽ tìm kiếm endltrong stdvà tìm thấy nó (trong <iostream>).

Và đây là kết quả được tạo ra bởi trình biên dịch (chế độ C ++ 11):

prog.cpp: In function ‘int main()’:
prog.cpp:4:36: error: ‘endl’ was not declared in this scope
 std::cout << "Hello, world" << endl;
                                ^

Đây là lỗi trong trình biên dịch hoặc trong sách. Tiêu chuẩn nói gì?

Cập nhật:

Tôi cần phải làm rõ một chút. Tôi biết rằng câu trả lời đúng là sử dụng std::endl. Câu hỏi là về văn bản trong cuốn sách. Như Lachlan Easton đã nói, nó không chỉ là lỗi đánh máy. Toàn bộ đoạn văn là (có thể) sai. Tôi có thể chấp nhận loại lỗi này nếu cuốn sách của một tác giả khác (ít được biết đến hơn), nhưng tôi (và vẫn đang) nghi ngờ vì nó được viết bởi Bjarne.


12
std::endlkhông có lỗi
aaronman

3
Theo kinh nghiệm của tôi, sách nổi tiếng với lỗi và lỗi chính tả. Hy vọng rằng chỉ nhỏ / hiển nhiên trong một cuốn sách hay.
Neil Kirk

31
@aaronman OP rõ ràng nhận thức được điều đó. Từ câu trích dẫn, có vẻ như Bjarne (người tạo ra C ++) đang tuyên bố rằng std::không bắt buộc trong trường hợp này, do ADL. Nhưng điều này không biên dịch, do đó câu hỏi.
BlueRaja - Danny Pflughoeft

6
Đúng vậy, vấn đề là, cuốn sách đã nói rõ điều sai trái. Đó không phải là lỗi đánh máy, cả một đoạn văn được viết để mô tả những gì không thực sự đúng. Đó là một lỗi trong cuốn sách.
DanielKO

7
@maverik Đây một lỗi trong cuốn sách. Tôi đã báo cáo vấn đề này với anh ấy vài phút trước, tôi sẽ cho bạn biết về câu trả lời của anh ấy.
Ali

Câu trả lời:


83

Nó không phải là một lỗi trong trình biên dịch. ADL được sử dụng để tra cứu các hàm không phải đối số . operator<<là hàm được tìm thấy thông qua ADL ở đây bằng cách xem các tham số std::coutvà (nên là gì) std::endl.


2
Trong thực tế, khi nhìn lại này truyền cảm hứng cho một cách để làm cho mã hợp lệ bằng cách khai thác thực tế là std::endllà như một vấn đề của thực tế (và gây nhầm lẫn) một hàm:endl(std::cout << "Hello, world"); // OK because of ADL
alfC

49

Đối với những người nói rằng đó là một lỗi đánh máy, nó không phải. Hoặc Bjarne đã mắc lỗi hoặc trình biên dịch đã sai. Đoạn sau đoạn văn do OP đăng

Nếu không có tra cứu phụ thuộc vào đối số, sẽ không tìm thấy trình thao tác endl. Như vậy, trình biên dịch nhận thấy rằng đối số đầu tiên của << là một ostream được định nghĩa trong std. Do đó, nó tìm kiếm endl trong std và tìm thấy nó (trong <iostream>).


18
Ngài dường như là người duy nhất ở đây thực sự đọc nó trên cuốn sách. Đó là một sự thay đổi đáng kể trong các quy tắc ngôn ngữ, khiến tất cả các trình biên dịch C ++ hiện tại trở nên không chuẩn (đối với C ++ 11), hoặc một lỗi rõ ràng từ ông Stroustrup (và không chỉ là lỗi đánh máy). Tôi thà đợi thêm hai tháng để có được một ấn bản sửa đổi. Tốt hơn là anh ấy nên mọc râu trở lại.
DanielKO

Nhân tiện, đánh dấu đã ăn điểm cuối cùng trong câu trích dẫn, bạn có thể muốn sử dụng dấu gạch ngược, "(in <iostream>)".
DanielKO

20

Đó là một lỗi đánh máy trong cuốn sách như những người khác đã chỉ ra. Tuy nhiên, ý nghĩa của cuốn sách là chúng ta phải viết

std::operator<<(std::cout, "Hello, world").operator<<(std::endl);

không có ADL. Đó là những gì Bjarne có nghĩa là chi tiết.


Tôi đứng sửa lại. Như Lachlan Easton đã chỉ ra, đó không phải là lỗi đánh máy mà là một sai sót trong cuốn sách. Tôi không có quyền truy cập vào cuốn sách này, đó là lý do tại sao tôi không thể đọc đoạn văn đó và tự nhận ra nó. Tôi đã báo cáo sai lầm này cho Bjarne để anh ấy sửa chữa.


Buồn cười. Ví dụ tương tự trên Wikipedia

Lưu ý rằng đó std::endllà một hàm nhưng nó cần đủ điều kiện, vì nó được sử dụng làm đối số cho operator<<( std::endllà một con trỏ hàm, không phải là một lời gọi hàm).

Không nghi ngờ gì nữa, đó là một sai sót trong cuốn sách. Tuy nhiên, ví dụ std::operator<<(std::cout, "Hello, world").operator<<(std::endl);cho thấy ADL giúp giảm độ dài dòng như thế nào.


Nhờ gx_ cho chỉ ra sai lầm của tôi .


Nó còn hơn cả một lỗi đánh máy, anh ấy đã nghĩ ra điều gì đó (cách tra cứu std::operator<<xảy ra) và viết cả một đoạn văn với thông tin không chính xác. Nó thực sự khiến bạn tin rằng các quy tắc ADL đã thay đổi và các trình biên dịch hiện đã bị hỏng.
DanielKO

thực sự có vẻ như có khá nhiều lỗi chính tả trong cuốn sách, ví dụ như 17.2.5
AndersK

@DanielKO Tôi đứng sửa lại; Tôi đã sửa câu trả lời của mình, cảm ơn. Tôi không có quyền truy cập vào cuốn sách này, đó là lý do tại sao tôi nghĩ đó là lỗi đánh máy. Trong mọi trường hợp, ADL thực sự giúp giảm độ dài và đoạn mã tôi đưa ra là một ví dụ về điều đó. Trong mọi trường hợp, cảm ơn đã cho tôi biết.
Ali

Trên thực tế, những gì chúng ta sẽ phải viết thực sự là std::operator<<(std::cout, "Hello, world").operator<<(std::endl);(xem không phải thành viênoperator<<thành viênoperator<< )
gx_

10

Gợi ý có trong tên "tra cứu phụ thuộc vào đối số".

Nó tìm kiếm các tên hàm không đủ tiêu chuẩn, hoạt động tùy thuộc vào các đối số .

Nó không liên quan gì đến việc tra cứu các đối số.

Bjarne bỏ lỡ.


8

Tôi không có cuốn sách, nhưng đây có vẻ là một lỗi trong cuốn sách, thực tế là nó thiếu bộ định tính không gian tên không liên quan gì đến ADL. Nó nên được std::endl.


1
Tôi đồng ý. Nhưng đây là một tuyên bố khá kỳ lạ (ý tôi là câu trong sách). Tôi hy vọng Bjarne nên biết về điều đó.
maverik

@maverik Có thể anh ấy đã làm vậy, tôi sẽ không ngạc nhiên khi ai đó đã báo cáo điều này. Nếu không, bạn có thể :)
Borgleader

@maverik nó chỉ là một lỗi đánh máy thực sự, tôi sẽ đoán ai đó đã nhận thấy nó
aaronman

2
Đúng, thực sự, tôi đã hiểu sai tất cả các tuyên bố (với std::cout) Anh ấy đang nói về việc tìm kiếm operator<<, không phải endl.
maverik

4

Vâng, đó là một lỗi - ví dụ này không đúng và không nên biên dịch. ADL áp dụng cho các tên hàm không đủ tiêu chuẩn giới thiệu các biểu thức gọi hàm. endllà một biểu thức id đang cố gắng tìm kiếm std::endl. endlkhông giới thiệu một biểu thức gọi hàm vì vậy tra cứu phụ thuộc đối số không được sử dụng cho nó, chỉ sử dụng tra cứu không đủ tiêu chuẩn, vì vậy nó sẽ không tìm thấy std::endlnhư dự định.

Một ví dụ đơn giản và chính xác sẽ là:

#include <vector>

int main()
{
    std::vector<int> x, y;
    swap(x,y); // calls std::swap due to ADL
}

Tóm lại, trước khi tra cứu một lệnh gọi hàm (ví dụ f(x,y,z)) với id không đủ tiêu chuẩn (ví dụ f), trước tiên các tham số của hàm (ví dụ x,y,z) được phân tích để xác định kiểu của chúng. Một danh sách các không gian tên liên quan được hình thành dựa trên các loại (ví dụ: không gian tên bao quanh định nghĩa của loại là một không gian tên được liên kết). Các không gian tên này sau đó được tìm kiếm bổ sung cho hàm.

Mục đích của ví dụ Bjarne là để hiển thị ADL của std::operator<<hàm, và không phải std::endl. Điều này đòi hỏi bạn phải hiểu thêm rằng các toán tử được nạp chồng trên thực tế là các biểu thức gọi hàm, vì vậy x << ycó nghĩa operator<<(x,y), và operator<<là một tên không đủ tiêu chuẩn, và do đó ADL áp dụng cho nó. Các loại LHS là std::ostreamđể stdlà một không gian tên có liên quan, và do đó std::operator<<(ostream&, ...)được tìm thấy.

Phần chú giải đã sửa chữa nên đọc:

Nếu không có tra cứu phụ thuộc vào đối số, sẽ không tìm thấy <<toán tử được nạp chồng trong stdkhông gian tên. Như vậy, trình biên dịch nhận thấy rằng đối số đầu tiên của << là một ostream được định nghĩa trong std. Do đó, nó tìm toán tử <<trong std và tìm thấy nó (trong <iostream>).

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.