2010-09-12 30 views
12

Mã này hoạt động:chức năng vs khai báo biến trong C++

std::ifstream f(mapFilename.c_str()); 
std::string s = std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); 
ParseGameState(s); 

Nhờ đó mà mapFilename là một std::stringvoid ParseGameState(const std::string&);.

Và đây không:

std::ifstream f(mapFilename.c_str()); 
std::string s(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); 
ParseGameState(s); 

Đây là lỗi:

game.cpp: In member function ‘int Game::LoadMapFromFile(const std::string&)’: 
game.cpp:423: error: no matching function for call to ‘ParseGameState(std::string (&)(std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> > (*)()))’ 
game.cpp:363: note: candidates are: ParseGameState(const std::string&) 

Vì vậy, có vẻ như nó nhận ra s như một tuyên bố chức năng và không phải là một khai báo biến trong trường hợp này.

Tại sao lại như vậy? Đây có phải là một lỗi trong GCC 4.2.1 (Apple xây dựng)? Hay GCC xử lý điều này một cách chính xác? Đây có phải là không xác định trong tiêu chuẩn C++?

Trả lời

14

Đây là "phân tích cú pháp gây tranh cãi nhất" của C++. Một Google nhanh chóng cho rằng sẽ bật lên rất nhiều lượt truy cập với rất nhiều chi tiết. Câu trả lời cơ bản là có, trình biên dịch coi nó là khai báo hàm - và C++ yêu cầu nó làm như vậy. Không có gì sai với trình biên dịch của bạn (ít nhất là trong khía cạnh này).

Nếu có bất kỳ sự thoải mái nào, bạn có rất nhiều công ty tốt trong việc điều hành điều này. Trong thực tế, nó là đủ phổ biến mà C + + 0x được thêm vào một cú pháp brace-initializer mới, phần lớn bởi vì nó tránh sự mơ hồ này. Sử dụng nó, bạn có thể viết một cái gì đó như:

std::string s{std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()}; 

Điều đó sẽ làm cho nó rõ ràng rằng các nội dung của niềng răng được dự định được giá trị cho khởi s, không loại tham số để một hàm có tên s. Tôi không biết nếu Apple có một cổng của nó chưa, nhưng gcc chấp nhận cú pháp mới như của phiên bản 4.5 (hoặc lâu hơn).

Chỉnh sửa: Lặp lại N3092, Johannes là (như thường lệ) khá chính xác. Ngôn ngữ áp dụng là (§8.5.4/3/5): "Nếu T có một hàm tạo danh sách khởi tạo, danh sách đối số bao gồm danh sách khởi tạo làm đối số duy nhất; nếu không, danh sách đối số bao gồm các phần tử của trình khởi tạo danh sách."

Vì vậy, kể từ std::string có một constructor khởi tạo danh sách, điều này sẽ cố gắng "công cụ" hai istreambuf_iterator s vào một danh sách initializer, và thông qua đó để ctor std::string mà phải mất một danh sách initializer - nhưng đó sẽ là một loại không khớp, vì vậy mã không thể biên dịch được. Đối với một số loại loại khác (không giống như std::string đã làm không có trình tạo hàm khởi tạo), phép chuyển đổi ở trên sẽ hoạt động (nhờ vào "cách khác ..." trong báo ở trên). Trong trường hợp của std::string, bạn phải sử dụng một trong các lựa chọn thay thế hiện tại chẳng hạn như std::string s = std:string(...).

Tôi xin lỗi vì đã sửa lỗi được đề xuất không chính xác - trong trường hợp này, điều này càng tồi tệ hơn vì nó gây nhầm lẫn một vấn đề có thể sẽ gây nhầm lẫn quá mức và nếu cần làm rõ. năm.

+1

Cảm ơn! [Ở đây trên WP] (http://en.wikipedia.org/wiki/Most_vexing_parse) cũng là một lời giải thích tốt về phân tích cú pháp khó chịu nhất. – Albert

+1

Dường như việc khai báo biến 'std :: string' ở trên sẽ không hoạt động trong C++ 0x: Bởi vì' std :: string' có một hàm tạo danh sách khởi tạo, toàn bộ '{...} 'sẽ được coi như một đối số, và sẽ cố gắng khởi tạo tham số' initializer_list 'và không biên dịch: ( –

+1

@Johannes: Yup, tôi nghĩ bạn nói đúng. Tôi gần như tự hỏi liệu nó có tốt hơn không nếu điều này được viết lại thay vì xem xét danh sách khởi tạo và sau đó (chỉ khi không có), hãy xem xét các ctors khác, nếu thay vào đó nó chỉ xây dựng một tập quá tải và chọn một kết hợp tốt nhất - có thể với điều chỉnh nhỏ mà tạo ra initializer_list sẽ tương đương với không có chuyển đổi liên quan đến tính tốt đẹp của trận đấu (với điều kiện mà tôi đã chỉ nghĩ về nó một vài phút, do đó có thể gây ra một vấn đề tinh tế (hoặc thậm chí là lóa) mà tôi đang thiếu) –

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