5

Tôi có một số loại có tiểu loại có cùng tên từng:Tuỳ chỉnh biên dịch thông báo lỗi khi không xác định kiểu phụ được truy cập

struct TypeA { 
    typedef int subtype; 
}; 
struct TypeB { 
    typedef float subtype; 
}; 

và cả các loại mà không có kiểu phụ này nhưng được sử dụng trong cùng một ngữ cảnh:

struct TypeC { 
    // (no subtype defined) 
}; 

Làm cách nào để thêm một loại phụ giả tạo thông báo lỗi biên dịch tùy chỉnh?

của tôi (cho đến nay không thành công) cố gắng là:

struct TypeC { 
    struct subtype { 
     static_assert(false, "Attempt to access the non-existent subtype of TypeC."); 
    }; 
}; 

Nhưng static_assert(false, ...) không thể làm việc, như trình biên dịch ném lỗi ngay cả khi loại không bao giờ truy cập.

Làm cách nào để trì hoãn việc đánh giá static_assert cho đến khi loại truy cập được truy cập?

Một thất bại nỗ lực là để giới thiệu một enum giả và xây dựng một biểu hiện của nó:

enum { X }; 
static_assert(X != X, "..."); 

trường hợp sử dụng bê tông: Tôi có một tầm cỡ mẫu List được định nghĩa với phụ loại headtail nếu không trống, và nên đưa ra một lỗi nếu các tiểu loại được sử dụng nếu nó là rỗng:

template<typename...> 
struct List; 

// empty list: 
template<> 
struct List<> { 
    struct head { static_assert(false, "Attempt to access the head of an empty list."); }; 
    struct tail { static_assert(false, "Attempt to access the tail of an empty list."); }; 
}; 

// non-empty list: 
template<typename Head, typename ...Tail> 
struct List<Head, Tail...> { 
    typedef Head head; 
    typedef List<Tail...> tail; 
}; 

Nếu tôi chỉ cần loại bỏ các loại headtail, khi truy cập ví dụ: phần tử thứ 3 của danh sách có kích thước 2 với mã số List<int,int>::tail::tail::head mang lại thông điệp không đẹp (g ++ 4.7.2): 'head' is not a member of 'List<int>::tail {aka List<>}'

+0

Ví dụ 'List <>' đó không phàn nàn về 'static_assert'? Tôi nghĩ rằng các biểu thức liên tục cần thiết để liên quan đến một tham số mẫu để tránh đánh giá ngay lập tức. – aschepler

+0

Hmm, dummy enum [dường như không hoạt động] (http://coliru.stacked-crooked.com/a/602ff84bdc70c08e). –

+0

@aschepler Nó hoạt động với g ++ 4.7.2, không chắc chắn về các trình biên dịch khác hoặc thậm chí là tiêu chuẩn. – leemes

Trả lời

5
// empty list: 
template<typename... Args> 
struct List { 
    struct head {static_assert(sizeof...(Args) != 0, "Attempt to access the head of an empty list."); }; 
    struct tail {static_assert(sizeof...(Args) != 0, "Attempt to access the tail of an empty list."); }; 
}; 

// non-empty list: 
template<typename Head, typename ...Tail> 
struct List<Head, Tail...> { 
    typedef Head head; 
    typedef List<Tail...> tail; 
}; 

Edit: Vấn đề này thực sự chạm vào ba khía cạnh của cách C++ mẫu làm việc:

  1. (§14.7.1 [temp.inst]/p1) Trừ một lớp mẫu chuyên môn đã được khởi tạo một cách rõ ràng (14.7.2) hoặc chuyên môn hóa rõ ràng (14.7.3), chuyên môn mẫu lớp được khởi tạo ngầm khi chuyên môn được tham chiếu trong ngữ cảnh yêu cầu một kiểu đối tượng được xác định hoàn toàn hoặc khi tính đầy đủ của loại lớp ảnh hưởng đến ngữ nghĩa của chương trình.Sự khởi tạo ngầm của một chuyên môn mẫu lớp gây ra sự khởi tạo ngầm của các khai báo, nhưng không phải của các định nghĩa ... của các hàm thành viên lớp, các lớp thành viên, [...].
  2. (§14.7.1 [temp.inst]/p11) Việc triển khai sẽ không diễn ra nhanh chóng ... một lớp thành viên ... của mẫu lớp không yêu cầu khởi tạo.
  3. (§14.6 [temp.res]/p8) Nếu không thể tạo chuyên môn hợp lệ cho mẫu và mẫu đó không được khởi tạo, mẫu không đúng định dạng, không cần chẩn đoán.

3) có nghĩa là sự biểu hiện static_assert phải phụ thuộc vào một mẫu đối số, vì nếu không "không chuyên môn hóa có giá trị" có thể được tạo ra cho mẫu và chương trình là vô hình thành, và trình biên dịch có thể tự do báo cáo lỗi (mặc dù họ không phải). Trong đoạn mã trên, một chuyên môn hợp lệ có thể được tạo ra cho mẫu đầu tiên, nhưng chuyên môn hóa đó không bao giờ được sử dụng vì chuyên môn hóa một phần.

Giải pháp được đưa ra ở trên cũng dựa trên 1) và 2). 1) quy định rằng ngầm instantiating một mẫu chuyên môn chỉ instantiates tờ khai (không định nghĩa) của các lớp học viên, và 2) có nghĩa là trình biên dịch được nhaát cấm cố gắng để nhanh chóng head hoặc tail nếu ai chỉ đơn thuần sử dụng một ngầm instantiated List<> . Lưu ý rằng quy tắc này không áp dụng nếu bạn nhanh chóng khởi tạo List<> với template struct List<>;.

Các giải pháp trong câu trả lời leemes của công trình vì typedef s không đòi hỏi một loại đầy đủ và do đó, không kích hoạt instantiation ngầm của SubTypeErrorMessage<> dưới 1), và việc sử dụng một mẫu đối số trong static_assert trong SubTypeErrorMessage bỏ qua 3), như một có thể tạo chuyên môn hợp lệ (ví dụ: SubTypeErrorMessage<true>) cho mẫu đó.

Cần lưu ý rằng trong cả hai trường hợp, quy tắc instantiation có nghĩa là vẫn hợp pháp để sử dụng List<>::head hoặc TypeC::subtype miễn là bạn không sử dụng chúng theo cách yêu cầu loại hoàn chỉnh. Vì vậy, một cái gì đó như int f(List<>::head &) { return 0; } là hợp lệ, mặc dù hoàn toàn vô nghĩa vì không có cách nào bạn thực sự có thể gọi chức năng đó. Tuy nhiên, nếu bạn không định nghĩa List<>::head, trình biên dịch sẽ báo cáo lỗi (có thể là khó hiểu) trên mã này. Vì vậy, đó là sự cân bằng cho các thông báo lỗi đẹp hơn :)

+0

Oh đẹp nhất, thậm chí có "logic chính xác" trong biểu thức;) (mặc dù trường hợp có kích thước lớn hơn 0 không bao giờ đạt được nó ...) Cảm ơn bạn cho giải pháp tốt đẹp này. – leemes

+0

Cảm ơn bạn rất nhiều vì đã giải thích chi tiết. Tóm lại, cả giải pháp của bạn và tôi đều được đảm bảo để hoạt động, tức là trình biên dịch không được phép đánh giá xác nhận sớm hơn tôi muốn nó được? – leemes

+0

@leemes Tôi tin như vậy. –

5

Để trì hoãn đánh giá static_assert đến điểm mà loại của bạn được truy cập, bạn phải làm cho biểu thức phụ thuộc vào thông số mẫu.

Một giải pháp khả thi là thêm một mẫu lớp helper chỉ để in các thông báo lỗi có điều kiện (tùy thuộc vào giá trị của các tham số mẫu):

template<bool X = false> 
struct SubTypeErrorMessage { 
    static_assert(X, "Attempt to access the non-existent subtype of TypeC."); 
}; 

Sau đó, trong các loại bê tông nơi bạn muốn có một "dummy sub-type":

struct TypeC { 
    typedef SubTypeErrorMessage<> subtype; 
}; 

Live Demo

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