Tôi chạy vào hành vi kỳ lạ này khi kiểm tra có hay không typename
là yêu cầu của clang. Cả hai clang và gcc chấp nhận mã này trong khi msvc từ chối nó.Tên tệp có thể được bỏ qua trong loại-specifier của định nghĩa thành viên ngoài dòng không?
template<class T1>
struct A
{
template<class T2>
struct B
{
static B f;
static typename A<T2>::template B<T1> g;
};
};
template<class T1>
template<class T2>
typename A<T2>::template B<T1> // ok, typename/template required
A<T1>::B<T2>::g;
template<class T1>
template<class T2>
A<T1>::B<T2> // clang/gcc accept, msvc rejects missing typename
A<T1>::B<T2>::f;
Nói chung, một trình độ-id A<T1>::B<T2>
(nơi A<T1>
là một cái tên phụ thuộc) nên được viết typename A<T1>::template B<T2>
. Là hành vi của gcc/clang không chính xác, hoặc là có một ngoại lệ cho quy tắc chung (trích dẫn dưới đây) trong trường hợp cụ thể này?
Có thể lập luận rằng A<T1>
không phải là tên phụ thuộc hoặc B<T2>
là một thành viên của phiên bản hiện tại. Tuy nhiên, tại thời điểm phân tích cú pháp kiểu-specifier thì không thể biết rằng instantiation hiện tại là A<T1>
. Có vẻ như có vấn đề khi yêu cầu triển khai để đoán rằng A<T1>
là phiên bản hiện tại.
14,6 Tên độ phân giải [temp.res]
Một tên được sử dụng trong một tuyên bố hoặc định nghĩa mẫu và đó là phụ thuộc vào một mẫu tham số là giả không nêu tên một loại trừ khi tra cứu tên áp dụng tìm thấy tên loại hoặc tên có đủ điều kiện bằng tên kiểu từ khóa.
14,2 Tên mẫu chuyên ngành [temp.names]
Khi tên của một thành viên mẫu chuyên môn xuất hiện sau khi
.
hoặc->
trong một postfix thể hiện hoặc sau một lồng nhau-tên-specifier trong một trình độ-id , và biểu thức đối tượng hoặc con trỏ của biểu thức postfix hoặc tên lồng nhau-specifier trong id đủ điều kiện phụ thuộc vào tham số mẫu (14.6.2) nhưng không tham chiếu đến một thành viên của phiên bản hiện tại (14.6. 2.1), tên mẫu thành viên phải được bắt đầu bằng từ khóa mẫu. Nếu không, tên được giả định là đặt tên cho một mẫu không phải là mẫu.
Tiếp tục điều tra những gì kêu vang đang làm ở đây, tôi cũng đã cố gắng này:
template<class T1>
struct C
{
template<class T2>
struct D
{
static typename A<T1>::template B<T2> f;
static typename A<T1>::template B<T2> g;
};
};
template<class T1>
template<class T2>
typename A<T1>::template B<T2> // ok, typename/template required
C<T1>::D<T2>::f;
template<class T1>
template<class T2>
A<T1>::B<T2> // clang rejects with incorrect error
C<T1>::D<T2>::g;
Clang cho error: redefinition of 'g' with a different type
, nhưng loại g
thực sự phù hợp với tuyên bố.
Thay vào đó, tôi sẽ thấy một chẩn đoán đề xuất việc sử dụng typename
hoặc template
.
Điều này cho thấy tín dụng cho giả thuyết rằng hành vi của kêu vang trong ví dụ đầu tiên là vô ý.
Cá nhân tôi sẽ thêm 'typename' ... nhưng không có ổ đĩa để đào sâu vào tiêu chuẩn vừa rồi :) –
@dribeas Đừng lo lắng;). Bạn phải mệt mỏi với tất cả những câu hỏi luật sư ngôn ngữ này ngay bây giờ! – willj
Bất kể điều nào là đúng hay sai, bạn xứng đáng nhận được một ưu đãi chỉ để tìm * bất kỳ * mã nào mà gcc và clang chấp nhận, nhưng VC++ từ chối trên cơ sở thiếu 'typename'. –