2013-08-20 18 views
12

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ô ý.

+0

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 :) –

+0

@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

+6

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'. –

Trả lời

1

clang và gcc là chính xác.

Trình biên dịch biết A<T1>::B<T2> đề cập đến một loại và B<T2> là mẫu và A<T1>::B<T2>::f là thành viên của bản đồ hiện tại. Do đó, các từ khóa typenametemplate là không cần thiết.

Từ v14.6.2.1p4:

Một tên là thành viên của instantiation hiện tại nếu nó là

Một trình độ-id trong đó lồng nhau-tên-specifier đề cập đến instantiation hiện và rằng, khi nhìn lên, đề cập với ít nhất một thành viên của instantiation hiện

A<T1>::B<T2> là một trình độ-id và A<T1>:: là lồng nhau-tên-specifier trong đó đề cập đến instantiation hiện hành. Chúng ta biết rằng A<T1>:: đề cập đến instantiation hiện từ 14.6.2.1p1:

Một tên dùng để chỉ instantiation hiện tại nếu nó là

- trong định nghĩa của một lớp mẫu chính hoặc thành viên của một lớp tiểu học mẫu, tên của lớp mẫu tiếp theo là mẫu danh sách đối số của mẫu chính (như mô tả dưới đây) kèm theo trong <> (hoặc một mẫu tương đương ali như chuyên môn),

Trong code của bạn, chúng tôi có một định nghĩa của một thành viên của một lớp mẫu tiểu học, ví dụ: A<T1>::B<T2>::f, và A<T1> là tên của mẫu lớp tiếp theo là danh sách mẫu tranh cãi của mẫu ban đầu.

Trong câu hỏi của bạn, bạn nói However, at the point of parsing the type-specifier it's not possible to know that the current instantiation is A<T1>. Tuy nhiên, tôi không thể làm theo điều đó vì tên A<T1> không tham chiếu đến bản trình bày hiện tại như đã nêu ở trên.

+0

Các quy tắc yêu cầu 'typename' (và' template') làm cho nó có thể cho trình phân tích cú pháp biết liệu một tên phụ thuộc là một kiểu hay một khuôn mẫu. Trình phân tích cú pháp tiến hành thông qua một luồng mã thông báo và phải đưa ra quyết định về việc liệu tên có phải là một loại (hay một mẫu) tại điểm mà một mã thông báo gặp phải. Để quyết định xem 'A :: B' có đặt tên lớp không có từ khóa 'typename' hay không, trình phân tích cú pháp sẽ phải xem trước một số mã thông báo có thể phát hiện rằng' A 'là phiên bản hiện tại. – willj

+0

@willj: Trong khi phần đầu tiên của nhận xét của bạn là chính xác, phần thứ hai thì không. Một số hình thức của tên xác định sự khởi tạo hiện tại. Trong trường hợp này, 'A ', và ':: A ' cả hai đều là tên hiện tại. Trình biên dịch xác định sự khởi tạo hiện tại theo cú pháp, xem 14.6.2.1p3 cho các ví dụ. –

+0

Xem [ví dụ này] (http://coliru.stacked-crooked.com/view?id=3fe1c0107d63f27f28792d7678c32b3a-25dabfc2c190f5ef027f31d968947336) và thông báo lỗi. Trình biên dịch ** biết ** 'A :: B' đặt tên một mẫu lớp vì nó đã tìm kiếm nó. –

1

MSVC là chính xác.

Việc đọc tiêu chuẩn C++ 11 của tôi gợi ý rằng yêu cầu typename.

Nếu không có từ khóa typename, tên phụ thuộc được giả định không đặt tên cho loại.

14,6 Tên độ phân giải [temp.res]

2) 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ừ trường hợp áp dụng tra cứu tên 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.

3) Khi id đủ điều kiện được dùng để chỉ một loại không phải là thành viên của phiên bản hiện tại và bộ định danh tên lồng nhau của nó đề cập đến loại phụ thuộc, nó sẽ được đặt trước bằng tên của từ khóa

7) trong định nghĩa của một lớp mẫu hoặc trong định nghĩa của một thành viên của một lớp mẫu sau các declarator-id, các typename từ khóa không cần thiết khi đề cập đến tên của một trước đó tuyên bố thành viên của mẫu lớp khai báo một loại.[Lưu ý: tên như vậy có thể được tìm thấy bằng tên không đủ tiêu chuẩn tra cứu, lớp thành viên tra cứu vào instantiation hiện, hoặc thành viên lớp truy cập biểu tra cứu khi kiểu của biểu thức đối tượng là instantiation hiện

14.6.2.1 phụ thuộc loại [temp.dep.type]

một tên dùng để chỉ instantiation hiện nếu nó là

  • trong định nghĩa của một lớp mẫu chính hoặc một thành viên của một chính lớp mẫu, tên của mẫu lớp tiếp theo là danh sách mẫu tranh cãi của mẫu tiểu học (như mô tả dưới đây) kèm theo trong <>

Khi A<T1> được sử dụng trong định nghĩa của một thành viên của A , nó đề cập đến sự khởi tạo hiện tại hiện tại. Khi phân tích cú pháp định nghĩa của f tên loại đủ điều kiện theo A<T1>:: có thể được tìm thấy bằng cách tra cứu tên thành viên lớp học vào bản trình bày hiện tại.

Tuy nhiên, khi trình phân tích cú pháp C++ gặp A<T1> trong kiểu trả về của định nghĩa hàm thành viên - trước khai báo-id - nó chưa gặp phải tên của lớp kèm theo. Trình phân tích cú pháp không thể xác định có hay không A đề cập đến lớp bao quanh tại thời điểm này. Vì lý do này - bất kể có hay không A<T1> đặt tên instantiation hiện tại - tiêu chuẩn không cho phép thiếu sót của typename trong định nghĩa của một thành viên của một mẫu lớp trước khi khai báo-id.

này example bởi Vaughn Cato chứng minh rằng hành vi của Clang/GCC là không phù hợp, và đòi hỏi typename trong một kịch bản tương tự:

template <typename T> 
struct A { 
    typedef int X; 
    X f(); 
}; 

template <typename T> 
A<T>::X A<T>::f() // error: missing 'typename' 
{ 
} 
Các vấn đề liên quan