2013-02-25 36 views
10

Tôi muốn xác minh rằng sau đây là lỗi trong GCC và không phải trong sự hiểu biết của tôi về C++. Xét đoạn mã sau:Xác minh lỗi trong GCC

struct A 
{ 
    struct B 
    { 
     template< typename U > U as() const { return U(); } 
    }; 

    B operator[](int) const { return B(); } 
}; 

template< typename T > 
struct as 
{ 
    template< typename U > 
    static T call(const U& u) 
    { 
     return u[ 0 ].as<T>(); // accepted by Clang 3.2, rejected by GCC 4.7 
     // return u[ 0 ].template as<T>(); // does not help and is IMHO not needed 
     // return u[ 0 ].A::B::as<T>(); // accepted by GCC 4.7 
    } 
}; 

int main() 
{ 
    as<int>::call(A()); 
} 

IMHO mã nên được tốt, nó được chấp nhận bởi Clang 3.2, nhưng không phải bởi GCC 4.7 (4.4 và 4.6 cũng thất bại với về cơ bản các lỗi tương tự nhưng 4.4 tạo ra một thông điệp hơi khác nhau) . Dưới đây là các đầu ra từ vỏ của tôi:

$ clang++-3.2 -O3 -Wall -Wextra -std=c++0x t.cc -o t 
$ g++-4.7 -O3 -Wall -Wextra -std=c++0x t.cc -o t 
t.cc: In static member function ‘static T as<T>::call(const U&)’: 
t.cc:17:21: error: invalid use of ‘struct as<T>’ 
t.cc: In static member function ‘static T as<T>::call(const U&) [with U = A; T = int]’: 
t.cc:18:4: warning: control reaches end of non-void function [-Wreturn-type] 
$ 

Câu hỏi: Đây có phải là một lỗi trong GCC hay tôi thiếu cái gì?

EDIT: Tôi hơi bối rối: Báo cáo lỗi GCC tại số http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55576 cho biết tại nhận xét số 9 rằng mã trong nhận xét số 3 là "hợp lệ". Chính xác điều này có nghĩa là gì? Có vẻ như những người GCC nghĩ rằng nó thực sự một lỗi, nếu không họ sẽ đóng nó? OTOH câu trả lời từ @Potatoswatter có vẻ khá rõ ràng rằng nó phải chính xác và tôi nên gửi báo cáo lỗi chống lại Clang (hoặc đã có báo cáo lỗi như vậy?)

Lưu ý rằng tôi ngần ngại đánh dấu câu trả lời là được chấp nhận cho đến khi trên được làm rõ. Vì cả hai câu trả lời đều hữu ích (một để giải thích, một câu trả lời cho công việc), tôi đã cho cả hai câu trả lời.

Câu hỏi về tiền thưởng: Vì tôi nhận được phiếu giảm giá cho mã không tường thuật, tôi tự hỏi người khác cảm thấy như thế nào. Tôi đã cố gắng để tạo ra một SCCEE mà loại bỏ tất cả các phiền nhiễu và tập trung vào các vấn đề kỹ thuật. Đó là cách tôi thích nghĩ về những điều này. Là sai đó?

Ngoài ra, @EdHeal: Tại sao mã dễ bị thiên tai? (Bạn không nghĩ đó là mã thế giới thực mà tôi có, phải không?)

EDIT2: Cảm ơn, David, vừa chú ý chỉnh sửa của bạn. Tôi sẽ đánh dấu câu trả lời của bạn là được chấp nhận ngay bây giờ và tôi cũng thấy rằng bạn đã nhận xét về báo cáo lỗi GCC. Tôi nghĩ rằng điểm chính của câu hỏi này là do đó được trả lời và GCC có một lời nhắc nhở khác. Cảm ơn mọi người.

+0

Tôi tin rằng đây là lỗi đã biết. – Potatoswatter

+4

Nhưng, như 'u [0]' là một biểu thức phụ thuộc, từ khóa 'template' là cần thiết. Trình phân tích cú pháp cần có khả năng tìm ra dấu hiệu '<' có nghĩa là không biết 'U' và không tìm kiếm một'> '. – Potatoswatter

+0

Đó là những gì tôi thấy là tốt, Clang nên từ chối mã. – Xeo

Trả lời

5

Đây là một góc khéo léo của ngôn ngữ. GCC đang áp dụng các quy tắc từ C++ 03 §3.4.5/1:

Trong một biểu thức truy cập thành viên lớp (5.2.5), nếu . hoặc -> token được ngay lập tức sau đó là một định danh theo sau là một <, số nhận dạng phải được tra cứu để xác định xem < có phải là bắt đầu của danh sách đối số mẫu hay không (14.2) hoặc toán tử nhỏ hơn. Định danh đầu tiên được tra cứu trong lớp của biểu thức đối tượng. Nếu không tìm thấy mã định danh, nó sẽ được tìm kiếm trong ngữ cảnh của toàn bộ biểu thức postfix và sẽ đặt tên cho một lớp hoặc mẫu hàm.Nếu tra cứu trong lớp của biểu thức đối tượng tìm mẫu, tên cũng được tra cứu trong ngữ cảnh của toàn bộ biểu thức postfix và

- nếu không tìm thấy tên, tên được tìm thấy trong lớp của biểu tượng được sử dụng, nếu không

- nếu tên được tìm thấy trong bối cảnh của toàn bộ postfix thể hiện và không đặt tên cho một lớp học mẫu, tên được tìm thấy trong lớp của biểu thức đối tượng được sử dụng, nếu không

- nếu tên được tìm thấy là một mẫu lớp, nó phải tham chiếu đến cùng một thực thể giống như tên được tìm thấy trong lớp của biểu thức đối tượng, nếu không chương trình không đúng định dạng.

Lưu ý rằng quá trình này là vô ích bởi vì từ khóa template đã được yêu cầu để disambiguate các < mã thông báo, kể từ khi loại các subexpression u[0] phụ thuộc vào một mẫu đối số.

Lý do thực hiện theo cách này là đơn giản hóa việc phân tích cú pháp trong trường hợp mẫu-id được sử dụng trong một vòng loại tên lồng nhau, ví dụ u[ 0 ].as<T>::bar.baz trong đó bar là typedef cho một lớp cơ sở.

C++ 11 loại bỏ ba điểm đạn đơn giản hoá quá trình để

Từ định danh là lần đầu tiên nhìn lên trong lớp của biểu thức đối tượng. Nếu không tìm thấy mã định danh, nó sẽ được tra cứu trong ngữ cảnh của toàn bộ biểu thức postfix và sẽ đặt tên cho một mẫu lớp.

Vì vậy, đây là lỗi và không phải lỗi cũ như tôi đã nói trước đây. Trường hợp cần tra cứu tên góc cần được xóa.

Ngoài ra, có vẻ như sự lừa đảo này có thể được khai thác để cho phép một biểu thức khuôn mẫu đơn luân phiên tham chiếu đến một lớp hoặc một hàm. Bạn không chắc liệu nó có hữu ích hay không, nhưng nó mới trong C++ 11.

+0

Đợi, vậy tên toàn cục 'as' thực sự được tìm thấy cho biểu thức lồng nhau? Wat. – Xeo

+0

@Xeo Yep, chỉ vì họ muốn tạo ra các công trình như contrivance của tôi trong đoạn cuối cùng. – Potatoswatter

+0

Đó có phải là tiêu chuẩn được công bố không? Tôi không thấy những viên đạn đó ở n3337 hoặc n3485. – ecatmur

1

Vấn đề là as được đưa vào phạm vi template<...> struct as làm cả tên lớp và tên mẫu; đây là lý do tại sao gcc phàn nàn "invalid use of ‘struct as<T>’".

Tôi không hoàn toàn chắc chắn cho dù gcc là chính xác (nó xuống để các quy tắc cho tra cứu tên trên biểu thành viên), nhưng cách giải quyết là sử dụng decltype:

return u[ 0 ].decltype(u[ 0 ])::template as<T>(); 
+0

Tên được tiêm không thể có giá trị ở đó, vì đó là tên lồng nhau không liên quan lớp học. – Xeo

+0

@Xeo Tôi không chắc chắn ý của bạn là "hợp lệ". Trình biên dịch không biết rằng 'U' không liên quan, và nó cũng không biết rằng nó có một mẫu thành viên' as'. – ecatmur