2016-04-26 23 views
6

Xét đoạn mã sau:biên dịch báo lỗi khi sử dụng CRTP với static_assert

template<typename Derived> 
struct Base { 
    static constexpr int x_base = Derived::x_derived; 
    //static_assert(x_base > 1, "Oops"); 
}; 

struct Derived : public Base<Derived> { 
    static constexpr int x_derived = 5 ; 
}; 

Base<Derived> obj; 

này biên dịch tốt trên gcc nhưng nếu tôi bỏ ghi chú dòng static_assert, nó than phiền rằng

error: incomplete type 'Derived' used in nested name specifier 
static constexpr int x_base = Derived::x_derived; 

Tôi đã thử nó với nhau phiên bản của gcc từ 4,9 đến 5,3 và tôi nhận được cùng một lỗi (bạn có thể thử nó trên godbolt here). kêu vang từ chối để biên dịch nó ngay cả khi không static_assert, và phàn nàn rằng

error: no member named 'x_derived' in 'Derived' 
static constexpr int x_base = Derived::x_derived; 

nào biên dịch là chính xác (nếu có)? Có cách nào tốt đẹp để sửa mã không?

Trả lời

9

Truy cập vào tên lồng nhau đòi hỏi các lớp học để được hoàn thành, nhưng Derived là không đầy đủ ở đây được nêu ra:

template<typename Derived> 
struct Base { 
    static constexpr int x_base = Derived::x_derived; 
            ^^^^^^^^^ 
}; 

để mã là vô hình thành.

Có một vài cách giải quyết. Trước tiên, bạn có thể riêng rẽ chỉ vượt qua trong các giá trị như một mẫu đối số:

template <typename Derived, int x_derived> 
struct Base { 
    static constexpr int x_base = x_derived; 
}; 

struct Derived : public Base<Derived, 5> { }; 

Thứ hai, nếu có thể (ví dụ như bạn không cần x_derived tuyên bố bất kỳ thành viên), bạn có thể di chuyển các giá trị vào một chức năng để trì hoãn instantiation của nó:

template<typename Derived> 
struct Base { 
    static constexpr int x_base() { 
     static_assert(Derived::x_derived > 1, "Oops"); 
     return Derived::x_derived; 
    } 

}; 

struct Derived : public Base<Derived> { 
    static constexpr int x_derived = 5; 
}; 
+0

Cảm ơn câu trả lời tuyệt vời Barry. Vì vậy, nếu tôi hiểu bạn một cách chính xác, thực tế là gcc chấp nhận mã mà không có static_assert là một lỗi trình biên dịch, và clang là chính xác trong việc từ chối nó? – toth

+1

@toth gcc không từ chối nó cho đến khi bạn thực sự sử dụng nó ở bất cứ nơi nào - vì vậy nó chỉ là một chút thân thiện hơn về khía cạnh đó. Nhưng nó không thực sự có ý nghĩa để chỉ cần khai báo một biến mà bạn không bao giờ sử dụng - do đó, nó từ chối nó một cách chính xác nơi nó quan trọng. – Barry

+0

nhưng gcc không cho phép tôi sử dụng nó, miễn là nó không nằm trong static_assert. Ví dụ: https://godbolt.org/g/rfbH5c (trong khi clang tiếp tục từ chối mã). Vì vậy, một trong những trình biên dịch phải là sai, bất kỳ ý tưởng nào? – toth

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