2015-11-17 35 views
12

xem xét như sau:Template alias tầm nhìn trong lớp lồng nhau

template<typename X> 
struct Z {}; 

struct A 
{ 
    using Z = ::Z<int>; 

    struct B : Z 
    { 
     using C = Z; 
    }; 
}; 

này biên dịch tốt. Tốt đẹp. Nhưng bây giờ thêm tham số khác trong Z:

template<typename X, typename Y> 
struct Z {}; 

struct A 
{ 
    template<typename X> 
    using Z = ::Z<X, int>; 

    struct B : Z<B> 
    { 
     using C = Z<B>; // error: too few template arguments for class template 'Z' 
    }; 
}; 

Ok, có lẽ nó có ý nghĩa rằng định nghĩa của mẫu alias Z trong lớp A có thể nhìn thấy khi thừa kế lồng lớp B, nhưng không phải bên trong cơ thể của nó, gây ra các lỗi kể từ khi định nghĩa toàn cầu của Z có hai tham số.

Nhưng lý do tại sao là hành vi khác trong trường hợp đầu tiên, khi Z chỉ là bí danh loại trong A?

Cuối cùng, hãy A mẫu:

template<typename X, typename Y> 
struct Z {}; 

template<typename T> 
struct A 
{ 
    template<typename X> 
    using Z = ::Z<X, int>; 

    struct B : Z<B> 
    { 
     using C = Z<B>; 
    }; 
}; 

Bây giờ lỗi đã biến mất. Tại sao?

(Thử nghiệm trên Clang 3.6 và GCC 4.9.2)

Trả lời

9

Nói tóm lại: Xuất phát từ một chuyên môn của Z giới thiệu tiêm-class-name của ::Z, được tìm thấy trước khi mẫu bí danh. Tuy nhiên, nếu A là mẫu, thì không thể tìm thấy tên lớp được tiêm nữa vì lớp cơ sở của B phụ thuộc.


xem xét [temp.local]/1:

Giống như (không mẫu) các lớp học bình thường, lớp mẫu có một tiêm-class-name (khoản 9). Các tiêm-class-name thể được sử dụng như một mẫu tên tuổi hoặc một loại tên.

Và [basic.lookup]/3:

Các tiêm-class-name của một lớp (khoản 9) cũng là coi là một thành viên của lớp cho các mục đích của tên […] tra cứu.

Z được xem là tên chưa đủ tiêu chuẩn; [Basic.lookup.unqual]/7:

enter image description here

Như vậy, tiêm-class-nameZ được tìm thấy như là một thành viên của lớp cơ sở Z<B, int>, và sử dụng như một mẫu tên tuổi , làm cho chương trình thứ hai của bạn bị hình thành.Trong thực tế, đoạn đầu tiên của bạn sử dụng tiêm-class-name cũng - đoạn sau đây sẽ không biên dịch:

struct A 
{ 
    using Z = ::Z<float>; 
    struct B : ::Z<int> 
    { 
     static_assert(std::is_same<Z, ::Z<float>>{}, ""); 
    }; 
}; 

Cuối cùng, nếu A được làm mẫu, lưu ý rằng B là một loại phụ thuộc như trên [temp.dep.type]/(9.3) , do đó Z<B> là loại phụ thuộc theo [temp.dep.type]/(9.7), do đó, lớp cơ sở Z<B> không được kiểm tra trong khi tra cứu unqualified-idZ theo [temp.dep]/3:

Trong định nghĩa của một lớp [..], phạm vi của một lớp cơ sở phụ thuộc (14.6.2.1) không được kiểm tra trong quá trình không đủ điều kiện tra cứu tên hoặc tại thời điểm định nghĩa của lớp mẫu hoặc thành viên hoặc trong quá trình khởi tạo mẫu hoặc thành viên của lớp.

Do đó, không thể tìm thấy tên lớp được tiêm.


B là một "lồng nhau lớp [..] đó là một phụ thuộc thành viên của instantiation hiện tại" (tôi nhấn mạnh), vì

Một tên là một phụ thuộc thành viên của phiên bản hiện tại nếu đó là thành viên của phiên bản hiện tại, khi tra cứu, đề cập đến số ít nhất một thành viên của một lớp là phiên bản hiện tại.

+0

Wow. Điều này khá rõ ràng, cảm ơn. Lỗi thực sự xuất hiện khi 'A' dừng lại là một mẫu, mà tôi nghĩ sẽ đơn giản hóa mã rất nhiều. Tuy nhiên, bây giờ tôi buộc phải sử dụng hai tên khác nhau cho hai chữ cái 'Z', điều này chỉ làm cho mã trở nên xấu hơn. Nếu có cách giải quyết nào tốt hơn, vui lòng cho tôi biết. – iavr

+0

@iavr Điều gì về 'sử dụng C = Z;'? (Sẽ không hoạt động cho 'A' là mẫu) – Columbo

+0

Bây giờ điều đó thật ấn tượng :-) Có, nó hoạt động trên mã được đơn giản hóa ở đây, nhưng không hoạt động trên mã gốc của tôi (' unknown type name 'Z''). Tôi sẽ phải kiểm tra sự khác biệt ở đâu. 'A' không phải là một mẫu nữa, và tôi dự định giữ nó theo cách đó. – iavr

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