2010-10-13 35 views
22

Đôi khi khi mã hóa với các mẫu C++, bạn muốn ngăn người dùng khởi tạo chuyên môn cụ thể hoặc tập hợp chuyên môn, vì kết quả sẽ không vô nghĩa. Vì vậy, bạn có thể định nghĩa một chuyên môn (cụ thể hoặc một phần) có định nghĩa, nếu được khởi tạo, sẽ gây ra lỗi trình biên dịch. Mục tiêu sẽ là, nếu người dùng "lạm dụng" mẫu, gây ra lỗi trình biên dịch ngay bên cạnh nhận xét trong tệp tiêu đề của bạn giải thích những gì không làm, thay vì để trình biên dịch đưa ra một thông báo lỗi khó hiểu thiết bị hoặc có thể cho phép mã có vấn đề để biên dịch.Làm thế nào để cố ý gây ra lỗi biên dịch trên bản mẫu instantation

Ví dụ:

template <typename T> struct MyClassTemplate { 
    // ... 
}; 

template <typename T> struct MyClassTemplate<T*> { 
    // Do not use MyClassTemplate with a pointer type! 
    typedef typename T::intentional_error err; 
}; 

Có một số cách để làm điều này (tùy thuộc vào việc chuyên môn của bạn là một chuyên môn hóa hoàn toàn hoặc một phần của một lớp hoặc chức năng). Nhưng cú pháp được sử dụng phải (?) Phụ thuộc vào tham số mẫu, hoặc trình biên dịch khác sẽ khiếu nại khi nó phân tích cú pháp định nghĩa lỗi cố ý đầu tiên. Ví dụ trên có một lỗ hổng trong đó ai đó có thể bướng bỉnh xác định một loại lồng nhau hoặc thành viên typedef intentional_error (mặc dù tôi muốn nói rằng sau đó họ sẽ xứng đáng với bất kỳ vấn đề nào nảy sinh). Nhưng nếu bạn sử dụng một mẹo quá ưa thích, bạn có thể nhận được thông báo lỗi trình biên dịch không thể giải mã và/hoặc gây nhầm lẫn, chủ yếu đánh bại mục đích.

Có cách nào đơn giản hơn để không cho phép instantiation mẫu không?

Tôi biết rằng trong C++ 0x, khái niệm mẫu và khai báo hàm đã xóa sẽ cung cấp khả năng kiểm soát tốt hơn đối với loại điều này, nhưng tôi đang tìm câu trả lời hợp lệ C++ 03.

Trả lời

25

Bạn chỉ có thể bỏ qua xác định nó.

template <typename T> struct MyClassTemplate<T*>; 

Bạn cũng có thể xuất phát từ một chuyên môn hóa không xác định

template <typename T> struct invalid; 
template <typename T> struct MyClassTemplate<T*> : invalid<T> { }; 

Lưu ý rằng chuyên môn rõ ràng rằng tuyên bố lớp học hoặc các chức năng sẽ không bao giờ phụ thuộc vào các thông số mẫu. Vì vậy, những thứ như thế này phụ thuộc vào tham số mẫu không thể hoạt động. Trong trường hợp đó, tuyên bố một chuyên môn hóa không được xác định rõ ràng nên là đủ

template<> struct MyClassTemplate<int*>; 
+0

Một số trong số này sẽ cung cấp lỗi tại thời gian liên kết chứ không phải là thời gian biên dịch phải không? – zwol

+0

@Zack tất cả sẽ cung cấp lỗi tại thời gian biên dịch. –

+0

Ồ, phải, bởi vì để khởi tạo một lớp mẫu một phải có khai báo lớp đầy đủ. Tôi đã suy nghĩ về các mẫu chức năng. – zwol

1

Các khái niệm đã bị xóa khỏi '0x. Bạn có thể sử dụng thư viện, như Boost Concept Check.

0

boost::enable_if

1

Nếu bạn không muốn sử dụng thư viện, xây dựng này là khá đáng tin cậy (đó là khoảng những gì Boost hoạt động trong nội bộ):

template <typename T> 
void must_be_specialized(T const&) 
{ 
    enum dummy { d = (sizeof(struct must_be_specialized_for_this_type) 
        == sizeof(T)) }; 
} 

Bạn có thể đặt một cái gì đó tương tự trong một chuyên môn để không cho phép tạo mẫu với loại đó. Tôi sẽ không, cá nhân, lo lắng về việc must_be_specialized_for_this_type đạt được một định nghĩa từ đâu đó, nhưng bạn có thể sử dụng một tờ khai chuyển tiếp để đưa nó đi trong một không gian tên riêng nếu bạn thực sự muốn.

+0

Tôi đã không nhận được nó, nếu hai loại có cùng kích thước những gì sẽ xảy ra? Ngoài ra phần nào với enum làm gì? –

+0

'struct must_be_specialized_for_this_type' không bao giờ được xác định ở bất cứ nơi nào, vì vậy đó là những gì gây ra lỗi. Sử dụng nó trong một biểu thức liên quan đến 'T' là những gì làm cho lỗi xảy ra tại thời điểm tạo mẫu thay vì phân tích cú pháp thời gian và phần' enum dummy' chỉ là một điều thuận tiện để thực hiện với một biểu thức liên tục. hoàn toàn ở thời gian phân tích cú pháp. – zwol

+0

Tôi đã không thực sự nhận thức được nó, nhưng cấu trúc đó thực sự là một tuyên bố về phía trước của một cấu trúc cục bộ của 'must_be_specialized'. Vì vậy, nó thực sự không thể có được một định nghĩa từ bên ngoài. Tuy nhiên, lưu ý rằng các trình biên dịch được tự do hóa ngay cả khi không có instantiating (comeau thực sự làm, và dgregor nói với tôi rằng tôi nên gửi một PR chống lại clang để chúng cũng vậy, sau khi chúng sửa các lỗi khác), bởi vì không bao giờ có thể là một chuyên môn hợp lệ bản mẫu. Bí quyết để làm cho nó hợp lệ sẽ được làm cho tên phụ thuộc, như 'sizeof (typename depedent :: type)'. –

12

Đối với tôi, âm thanh này giống như trường hợp điển hình cho static_assert từ C++ 0x hoặc BOOST_STATIC_ASSERT. Chức năng static_assert có lợi thế là bạn có thể chuyển thông báo lỗi tùy chỉnh để lý do lỗi rõ ràng hơn.

Cả hai cách đều mang đến cho bạn cơ hội sớm kết thúc quá trình biên dịch theo một số điều kiện thời gian biên dịch được xác định tùy chỉnh.

với static_assert:

template <typename T> struct MyClassTemplate<T*> { 
    static_assert(always_false<T>::value, "Do not use MyClassTemplate with a pointer type!"); 
}; 

với BOOST_STATIC_ASSERT

template <typename T> struct MyClassTemplate<T*> { 
    // Do not use MyClassTemplate with a pointer type! 
    BOOST_STATIC_ASSERT(always_false<T>::value); 
}; 

Luôn giả sẽ giống như thế này:

template< typename T > 
struct always_false { 
    enum { value = false }; 
}; 

HTH

Edit: Cố định các kỳ thi ples để làm cho họ thực sự làm việc ;-) Nhờ GMan!

+4

Những người đó sẽ khẳng định vô điều kiện, mọi lúc. (Nghĩa là, trình biên dịch sẽ thấy 'false', biết xác nhận tĩnh này không thành công, do đó kích hoạt chẩn đoán.) Bạn cần làm cho nó phụ thuộc vào tham số mẫu nếu bạn muốn nó chỉ kích hoạt khi được khởi tạo, như sau:' static_assert (sizeof (T) == 0, "blah"); '. – GManNickG

+0

Dang, tôi hoàn toàn quên rằng -.- 'Cảm ơn bạn đã nhắc tôi – Vinzenz

+2

Vì vậy, tôi đã khắc phục ví dụ rằng nó thực sự hoạt động :-) – Vinzenz

1

"Có cách nào đơn giản hơn để không cho phép instantiations mẫu không?" Không có gì tốt hơn đáng kể so với những gì bạn đã xác định. Tôi khá chắc chắn cơ chế bảo vệ C++ đang có để bảo vệ bạn khỏi tai nạn không phải từ ác ý. Và ai đó xác định một chuyên môn hoặc một lớp học để phá vỡ mục đích sử dụng của bạn, tôi sẽ xem xét độc hại. Có lẽ bạn có thể đánh người vào sau đầu mỗi khi họ làm điều đó.

Cá nhân tôi thích đặt séc vào các mẫu chỉ tồn tại để mô tả các kiểm tra. Điều đó cho phép kết hợp thú vị của kế thừa và mẫu.

template <class T> 
class not_with_pointer_t { }; 

template <class T> 
class not_with_pointer_t<T*>; 

template <class T> 
class some_class_t : public not_with_pointer_t<T> { }; 

template <class T, template <class U> class base_t> 
class another_class_t : public base_t<T> { }; 

typedef some_class_t<int> a_t; // ok 
typedef some_class_t<void*> b_t; // error if instantiated 
typedef another_class_t<void*, not_with_pointer_t> c_t; // error if instantiated 

template <class T> class unrestricted_t { }; 
typedef another_class_t<void*, unrestricted_t> d_t; // ok 
Các vấn đề liên quan