44

Giả sử tôi muốn viết hàm chung void f<T>(), có một điều nếu T là loại POD và một điều khác nếu T không phải là POD (hoặc bất kỳ biến vị ngữ tùy ý nào khác).Công văn thẻ so với phương pháp tĩnh trên các lớp chuyên ngành một phần

Một cách để đạt được điều này sẽ được sử dụng một mô hình thẻ-văn như thư viện tiêu chuẩn nào với loại iterator:

template <bool> struct podness {}; 
typedef podness<true> pod_tag; 
typedef podness<false> non_pod_tag; 

template <typename T> void f2(T, pod_tag) { /* POD */ } 
template <typename T> void f2(T, non_pod_tag) { /* non-POD */ } 

template <typename T> 
void f(T x) 
{ 
    // Dispatch to f2 based on tag. 
    f2(x, podness<std::is_pod<T>::value>()); 
} 

Một thay thế sẽ được sử dụng chức năng thành viên tĩnh của các loại phần chuyên ngành:

template <typename T, bool> struct f2; 

template <typename T> 
struct f2<T, true> { static void f(T) { /* POD */ } }; 

template <typename T> 
struct f2<T, false> { static void f(T) { /* non-POD */ } }; 

template <typename T> 
void f(T x) 
{ 
    // Select the correct partially specialised type. 
    f2<T, std::is_pod<T>::value>::f(x); 
} 

Ưu điểm và nhược điểm của việc sử dụng một phương pháp so với phương pháp khác là gì? Bạn sẽ giới thiệu cái nào?

+3

Bất cứ điều gì làm nổi thuyền của bạn. Tôi tìm thấy phiên bản thứ hai "typetraity" và hấp dẫn, bởi vì có ít mã phụ trợ hơn và ít khái niệm ẩn hơn. Ngoài ra tôi muốn thêm chuyển tiếp cho các đối số! –

Trả lời

15

Tôi muốn tag văn vì:

  • Dễ dàng mở rộng với các thẻ mới
  • Dễ sử dụng thừa kế (example)
  • là kỹ thuật khá phổ biến trong lập trình chung chung

Có vẻ khó khăn khi tôi thêm biến thể thứ ba trong ví dụ thứ hai. Khi bạn muốn thêm, ví dụ: loại POD-of-PODs không phải bạn sẽ phải thay thế bool trong template <typename T, bool> struct f2; bằng thứ khác (int nếu bạn thích =)) và thay thế tất cả struct f2<T, bool-value> bằng struct f2<T, another-type-value>. Vì vậy, đối với tôi, biến thể thứ hai trông khó có thể mở rộng được. Hãy sửa tôi nếu tôi sai.

+0

Một cách để mở rộng nó sẽ là sử dụng một tham số mẫu enum, nhưng tôi đồng ý rằng nó sẽ là một chút clunky. –

15

Một có thể đọc được thay thế cho [boost|std]::enable_if, thẻ và chuyên môn hóa một phần cho đơn giản thời gian biên dịch công văn mà tôi thích như sau:

[Hãy nhớ rằng các phép toán luận có chuyển đổi sang số nguyên, mà mảng zero-length không hợp lệ và các mẫu vi phạm bị loại bỏ (SFINAE). Ngoài ra, char (*)[n] là một con trỏ đến một mảng của n yếu tố này.]

template <typename T> 
void foo(T, char (*)[is_pod<T>::value] = 0) 
{ 
    // POD 
} 

template <typename T> 
void foo(T, char (*)[!is_pod<T>::value] = 0) 
{ 
    // Non POD 
} 

Nó cũng có lợi thế là không cần lớp bên ngoài gây ô nhiễm không gian tên. Bây giờ, nếu bạn muốn ra bên ngoài vị như thế nào trong câu hỏi của bạn, bạn có thể làm:

template <bool what, typename T> 
void foo(T, char (*)[what] = 0) 
{ 
    // taken when what is true 
} 

template <bool what, typename T> 
void foo(T, char (*)[!what] = 0) 
{ 
    // taken when what is false 
} 

Cách sử dụng:

foo<std::is_pod<T>::value>(some_variable); 
+3

+1 Rất thông minh. – templatetypedef

+0

Vâng, nó thông minh, nhưng nếu bạn không biết về SFINAE thì điều này có một yếu tố nghiêm trọng 'WTF' với nó mà những người khác không có. Tôi đánh giá cao tính ngắn gọn mặc dù. –

+1

@Peter: SFINAE đã có một yếu tố WTF lớn. Sử dụng 'typename std :: enable_if :: type' thực sự làm xáo trộn phần' bất cứ điều gì '. Ở đây nó đứng rõ ràng, bên trong dấu ngoặc đơn của nó. –

9

Thực ra cả hai đều chỉ là mẫu gửi thẻ. Trước đây được gọi là gửi thẻ theo trường hợp và sau đó là gửi thẻ theo loại.

Barend, tác giả chính của Boost.Geometry, explains cả hai phương pháp và ưu tiên thứ hai. Điều này được sử dụng trong Boost.Geometry rộng rãi.Here're những ưu điểm tóm tắt:

  • Nó là cần thiết để nhanh chóng các thẻ vì mục đích duy nhất của nó là để phân biệt
  • Nó rất dễ dàng để xác định các loại mới và hằng số dựa trên thẻ
  • luận cứ có thể được đảo ngược trong giao diện tức là nói distance(point, polygon);distance(polygon, point); cả hai có thể chỉ có một thi
1

tôi biết đây là một câu hỏi cũ với câu trả lời đã được chấp nhận, nhưng điều này có thể là một thay thế khả thi:

012.
template<typename T> 
std::enable_if_t<std::is_pod<T>::value> f(T pod) 
{ 
} 

template<typename T> 
std::enable_if_t<!std::is_pod<T>::value> f(T non_pod) 
{ 
} 
Các vấn đề liên quan