2008-09-23 35 views
49

Trong C# chúng ta có thể định nghĩa một kiểu generic áp đặt các ràng buộc trên các kiểu có thể được sử dụng làm tham số chung. Ví dụ sau minh họa việc sử dụng các ràng buộc chung:Ràng buộc bản mẫu C++

interface IFoo 
{ 
} 


class Foo<T> where T : IFoo 
{ 
} 

class Bar : IFoo 
{ 
} 

class Simpson 
{ 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Foo<Bar> a = new Foo<Bar>(); 
     Foo<Simpson> b = new Foo<Simpson>(); // error CS0309 
    } 
} 

Có cách nào chúng ta có thể áp đặt các ràng buộc cho các tham số mẫu trong C++ hay không.


C++ 0x có hỗ trợ gốc cho điều này nhưng tôi đang nói về tiêu chuẩn hiện tại C++.

+0

Như Nemanja nói, Boost có một thư viện mà thực hiện một cái gì đó tương tự. Đó là một chút chi tiết hơn nếu nó đã được một tính năng ngôn ngữ thích hợp, nhưng nó hoạt động, và nó cho phép bạn thể hiện hầu hết các khó khăn. – jalf

Trả lời

31

Như người khác đã đề cập, C++ 0x đang được tích hợp sẵn vào ngôn ngữ. Cho đến lúc đó, tôi khuyên bạn nên Bjarne Stroustrup 's suggestions for template constraints.

Chỉnh sửa: Boost cũng có alternative of its own.

Chỉnh sửa2: Hình như concepts have been removed from C++0x.

+2

... và có thể được thêm lại vào [C++ 1y] (http://stackoverflow.com/questions/15669592/what-are-the-differences-between-concepts-and-template-constraints) – Yakk

1

Chỉ ngầm.
Bất kỳ phương pháp nào bạn sử dụng trong phương thức thực sự được gọi là được áp đặt cho tham số mẫu.

8

Check-out Boost

Các Boost Khái niệm Kiểm tra Thư viện (BCCL)

Khái niệm Kiểm tra thư viện cho phép một để thêm tuyên bố rõ ràng và kiểm tra của concepts trong phong cách của proposed C++ language extension.

2

Sắp xếp. Nếu bạn static_cast đến một IFoo *, thì sẽ không thể khởi tạo mẫu trừ khi người gọi vượt qua một lớp có thể được gán cho một IFoo *.

31

"Ngụ ý" là câu trả lời đúng. Mẫu có hiệu quả tạo ra một kịch bản "vịt gõ", do cách thức mà chúng được biên soạn. Bạn có thể gọi bất kỳ hàm nào bạn muốn khi giá trị được nhập mẫu và các phiên âm duy nhất sẽ được chấp nhận là các hàm mà phương thức đó được xác định. Ví dụ:

template <class T> 
int compute_length(T *value) 
{ 
    return value->length(); 
} 

Chúng ta có thể gọi phương thức này trên một con trỏ đến bất kỳ loại mà tuyên bố length() phương pháp để trả lại một int. Thusly:

string s = "test"; 
vector<int> vec; 
int i = 0; 

compute_length(&s); 
compute_length(&vec); 

... nhưng không phải trên một con trỏ tới một kiểu mà không không tuyên bố length():

compute_length(&i); 

Ví dụ thứ ba này sẽ không biên dịch.

Điều này làm việc vì C++ biên dịch một phiên bản mới của hàm templatized (hoặc lớp) cho mỗi phiên âm. Khi nó thực hiện việc biên dịch, nó tạo ra sự thay thế trực tiếp, gần giống như macro đối với sự khởi tạo mẫu thành mã trước khi kiểm tra kiểu. Nếu mọi thứ vẫn hoạt động với mẫu đó, thì số tiền thu được sẽ được tính và cuối cùng chúng tôi sẽ nhận được kết quả. Nếu bất kỳ điều gì không thành công (như int* không khai báo length()), thì chúng tôi sẽ gặp phải lỗi biên dịch mẫu trang sáu lỗi đáng sợ.

+1

Tôi đoán bạn đang nói rằng nó đủ để viết nó trong một bình luận? '// T phải thừa kế từ BaseA, nếu không thì trình biên dịch sẽ thất bại ' – bobobobo

+0

' thì chúng ta sẽ nhận được lỗi biên dịch mẫu sáu trang đáng sợ' (lol) vì điều này tất cả chúng ta đang chờ khái niệm "khái niệm" linh thiêng. – TechNyquist

14

Bạn có thể đặt một loại bảo vệ trên IFoo mà không làm gì, chắc chắn rằng nó có trên T trong Foo:

class IFoo 
{ 
public: 
    typedef int IsDerivedFromIFoo; 
}; 

template <typename T> 
class Foo<T> 
{ 
    typedef typename T::IsDerivedFromIFoo IFooGuard; 
} 
0

Bạn có thể làm điều đó. Tạo mẫu cơ sở. Làm cho nó chỉ có các nhà xây dựng riêng. Sau đó, tạo chuyên môn cho từng trường hợp bạn muốn cho phép (hoặc làm ngược lại nếu danh sách không được phép nhỏ hơn nhiều so với danh sách được phép).

Trình biên dịch sẽ không cho phép bạn tạo nhanh các mẫu sử dụng phiên bản với các hàm tạo riêng tư.

Ví dụ này chỉ cho phép instantiation với int và float.

template<class t> class FOO { private: FOO(){}}; 

template<> class FOO<int>{public: FOO(){}}; 

template<> class FOO<float>{public: FOO(){}}; 

Đây không phải là cách làm ngắn gọn và thanh lịch, nhưng có thể.

-1

Nhìn vào mẫu CRTP (Mẫu mẫu đệ quy tò mò). Nó được thiết kế để hỗ trợ sự kế thừa tĩnh.

+0

CRTP không thực sự được thiết kế cho bất cứ điều gì cụ thể ... Tôi nghĩ rằng nó được _discovered_ chứ không phải là thiết kế (["Tên của thành ngữ này được đặt ra bởi Jim Coplien, người đã quan sát nó trong một số mã mẫu C++ sớm nhất." ] (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)), mặc dù đặt ràng buộc vào các tham số mẫu chắc chắn là một sử dụng nó xảy ra là rất tốt tại. – leftaroundabout

30

Nếu bạn sử dụng C++ 11, bạn có thể sử dụng static_assert với std::is_base_of cho mục đích này.

Ví dụ,

#include <type_traits> 

template<typename T> 
class YourClass { 

    YourClass() { 
     // Compile-time check 
     static_assert(std::is_base_of<BaseClass, T>::value, "type parameter of this class must derive from BaseClass"); 

     // ... 
    } 
} 
+2

Hoàn hảo! * và tôi cần thêm ký tự ... * –

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