2010-12-23 20 views
17

Có thể trong C++ để kiểm tra loại được chuyển vào một chức năng mẫu không? Ví dụ:Chuyển loại được truyền từ mẫu

template <typename T> 
void Foo() 
{ 
    if (typeof(SomeClass) == T) 
     ...; 
    else if (typeof(SomeClass2) == T) 
     ...; 
} 

Trả lời

27

Có, đó là ... nhưng nó có thể sẽ không hoạt động theo cách bạn mong đợi.

template < typename T > 
void foo() 
{ 
    if (is_same<T,SomeClass>::value) ...; 
    else if (is_same<T,SomeClass2>::value) ...; 
} 

Bạn có thể nhận is_same từ std:: hoặc boost:: tùy thuộc vào mong muốn của bạn/biên dịch. Cái cũ chỉ có trong C++ 0x.

Sự cố xảy ra với nội dung trong số .... Nếu bạn mong đợi để có thể thực hiện một số chức năng gọi cụ thể cho những loại trong foo, bạn đang buồn bã nhầm lẫn. Lỗi trình biên dịch sẽ cho kết quả mặc dù phần mã đó không bao giờ chạy khi bạn truyền vào một thứ không tuân theo giao diện mong đợi đó.

Để giải quyết vấn đề này, bạn cần phải làm điều gì đó khác biệt một chút. Tôi khuyên bạn nên tag dispatching:

struct v1_tag {}; 
struct v2_tag {}; 

template < typename T > struct someclass_version_tag; 
template < > struct someclass_version_tag<SomeClass> { typedef v1_tag type; }; 
template < > struct someclass_version_tag<SomeClass2> { typedef v2_tag type; }; 

void foo(v1_tag) { ... } 
void foo(v2_tag) { ... } 
template < typename T > void foo() 
{ 
    typedef typename someclass_version_tag<T>::type tag; 
    foo(tag()); 
} 

Lưu ý rằng bạn sẽ không thể bị bất kỳ chi phí runtime-đa hình ở đây và với tối ưu hóa bật nó nên kết quả trong cùng một hoặc thậm chí nhỏ hơn kích thước mã AND tốc độ (mặc dù bạn shouldn' Đừng lo lắng về điều đó cho đến khi bạn đã chạy một profiler).

+0

Có thể trả về các loại khác nhau dựa trên version_tag không? – Boying

+0

Chắc chắn. Bạn cần phải tìm ra một số cách để truy vấn thông tin nếu bạn đang mắc kẹt trong C++ 03, hoặc sử dụng tự động trong C++ 11 trở lên. Vì vậy, miễn là bạn không cố gắng trả lại các loại khác nhau trong cùng một instantiation bạn sẽ không có vấn đề. –

+0

ngay bây giờ với C++ 17, bạn có thể sử dụng lệnh constexpr if để làm điều này một cách tầm thường – xaxxon

10

Nếu bạn muốn làm một cái gì đó cụ thể dựa trên các loại, chuyên các mẫu:

template <typename T> 
void Foo() { } 

template <> 
void Foo<SomeClass>() { } 

template <> 
void Foo<SomeClass2>() { } 

// etc. 

(Bạn không thực sự muốn chuyên chức năng template, mặc dù, đây là Để có thêm thông tin về lý do và cách tránh các mẫu chức năng chuyên biệt, hãy đọc Why Not Specialize Function Templates?) của Herb Sutter,

2

Có. Bạn sẽ phải sử dụng type traits. Ví dụ:

#include <boost/type_traits/is_same.hpp> 

template <typename T> 
void Foo() 
{ 
    if ((boost::is_same<T, SomeClass>::value)) 
     ...; 
    else if ((boost::is_same<T, SomeClass2>::value)) 
     ...; 
} 

Tùy thuộc vào những gì bạn đang cố gắng để đạt được, sử dụng template specialization có thể là lựa chọn tốt hơn nhiều.

Ngoài ra, bạn có thể sử dụng enable_if/disable_if để có điều kiện bật/tắt một số chức năng/phương pháp nhất định. Kết hợp điều này với các đặc tính kiểu sẽ cho phép, ví dụ, sử dụng một hàm cho một tập các kiểu và một hàm khác cho một tập các kiểu khác.

+0

Mã của bạn sẽ không biên dịch. Bạn cần phải khởi tạo việc khởi tạo is_same dưới dạng biến, hoặc bạn cần truy cập định nghĩa 'value' bên trong của nó. –

+0

@Noah: Đúng vậy. Đây là một gợi ý nhanh chóng và bẩn :-) Tôi đã sửa đổi nó. –

+0

Có thể thực hiện điều này cho một tập hợp các giá trị không? Ví dụ: làm điều gì khác cho mỗi giá trị enum? – paulm

2

Không, tuy nhiên bạn có thể sử dụng đặc tả từng phần:

template<typename T> 
struct Bar { static void foo(); }; 
template<typename T> 
template<> inline void Bar<T>::foo() { 
//generic 
} 
template<> inline void Bar<int>::foo() { 
//stuff for int 
} 
template<> inline void Bar<QString>::foo() { 
//QString 
} 

Sửa Có với những đặc điểm loại, tuy nhiên nó không thực sự cần thiết. Chỉnh sửa 2 ví dụ type_traits.

#include <type_traits> 
template<typename T> void foo() { 
    using std::is_same; 
    if<is_same<T, T2>::value || is_same<T, T1>::value) { 
     /* stuff */ 
    } 
} 
+0

Nếu tôi muốn làm điều gì đó như 'if (is_same || is_same )'? –

+0

Sau đó, bạn phải sử dụng các đặc điểm kiểu '#include và std :: is_same :: value' nếu trình biên dịch của bạn hỗ trợ C++ 0x (VS2010/bất kỳ gcc gần đây) hoặc chỉ sử dụng tăng cho khả năng tương thích trình biên dịch cũ. – OneOfOne

+3

Đó không phải là một phần chuyên môn hóa, đó là chuyên môn đầy đủ. Có một sự khác biệt khá đáng kể. Có lẽ quan trọng nhất là nó sẽ không thể làm một phần chuyên môn theo cách đó vì nó không được phép với các chức năng của bất kỳ loại. –

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