2010-02-04 44 views
6

Tôi đọc C++ Primer, và nó nói chuyên môn về chức năng mẫu là một chủ đề nâng cao, nhưng tôi hoàn toàn bị mất. Ai có thể đưa ra một ví dụ tại sao chức năng chuyên môn hóa mẫu là quan trọng và cần thiết?Chức năng chuyên môn về mẫu chức năng và sự cần thiết

Tại sao các mẫu chức năng không hỗ trợ chuyên môn hóa một phần trong khi các mẫu lớp làm gì? Logic cơ bản là gì?

+1

Xin vui lòng, chúng ta có thể tránh cuộc tranh luận tiếng Anh và Mỹ không? Ai quan tâm nếu một số người sử dụng 's' và một số khác là' z'? –

+1

:) Tôi thấy nó trông như thế nào ... Nhưng trước đây câu hỏi cho biết "đặc tả mẫu". Tôi không quan tâm đến s so với z. Vẫn xóa bình luận vì nó không còn cần thiết nữa. –

Trả lời

0

Về cơ bản, ý tưởng là bạn có thể viết các mẫu hoạt động theo cách chung cho trường hợp chung, nhưng vẫn có thể xử lý các trường hợp đặc biệt. Một ví dụ về nơi chuyên môn được sử dụng là trong std::vector. std::vector<bool> là một chuyên ngành bao gồm các phần tử bool sao cho chúng chỉ sử dụng một bit cho mỗi phần tử chứ không phải một byte. std::vector<T> hoạt động giống như mảng động thông thường cho tất cả các loại khác.

Sử dụng nâng cao hơn cho chuyên môn là lập trình meta. Ví dụ, đây là một ví dụ (từ Wikipedia) về cách sử dụng chuyên môn mẫu để tính toán giai thừa tại thời gian biên dịch.

template <int N> 
struct Factorial 
{ 
    enum { value = N * Factorial<N - 1>::value }; 
}; 

template <> 
struct Factorial<0> 
{ 
    enum { value = 1 }; 
}; 
+7

Curses, 'vector '. – GManNickG

+0

Yeh, vector không phải là ví dụ lớn nhất :) –

+8

Không * chức năng * chuyên môn mẫu đã được hỏi về. –

8

Câu hỏi của bạn tại sao các chức năng không hỗ trợ chuyên môn hóa một phần có thể được trả lời here. Mã dưới đây cho thấy cách triển khai các chuyên môn khác nhau.

template<typename T> 
bool Less(T a, T b) 
{ 
    cout << "version 1 "; 
    return a < b; 
} 
// Function templates can't be partially specialized they can overload instead. 
template<typename T> 
bool Less(T* a, T* b) 
{ 
    cout << "version 2 "; 
    return *a < *b; 
} 

template<> 
bool Less<>(const char* lhs, const char* rhs) 
{ 
    cout << "version 3 "; 
    return strcmp(lhs, rhs) < 0; 
} 

int a = 5, b = 6; 

cout << Less<int>(a, b) << endl; 
cout << Less<int>(&a, &b) << endl; 
cout << Less("abc", "def") << endl; 
+0

Bạn có thể thả '' vào hai lệnh gọi đầu tiên trong báo cáo đầu ra. –

+1

Bạn có thể làm cho nó rõ ràng hơn: thường chuyên môn hóa không cần thiết cho chức năng vì đã quá tải cho phép một biểu tượng biểu diễn nhiều hàm và một cơ chế quyết định để chọn (giải quyết) có chức năng sử dụng dựa trên bộ quy tắc. –

+1

Liên kết tuyệt vời. Đối với tôi, điểm quan trọng nhất của bài báo đó là các chuyên môn về chức năng không ảnh hưởng đến * chức năng nào được gọi - chúng chỉ được chơi * sau * các kiểu mẫu cuối cùng đã được quyết định. Quá tải OTOH giới thiệu một "đối thủ cạnh tranh" mới vào "chức năng nào chúng ta nên gọi là" cạnh tranh. –

1

Để minh họa lý do chuyên môn hóa mẫu chức năng quan trọng, hãy xem xét hàm mẫu std::swap. Theo mặc định, về cơ bản không std::swap(x, y):

T temp = x; 
x = y; 
y = temp; 

nhưng điều này có thể không hiệu quả vì nó liên quan đến việc tạo ra một bản sao của x và có thể làm thêm sao chép trong các bài tập. Điều này đặc biệt xấu nếu x lớn (ví dụ: nếu là std::vector có nhiều yếu tố). Ngoài ra, mỗi dòng trên có thể thất bại và ném ngoại lệ, có khả năng để lại xy ở trạng thái không ổn định, không ổn định.

Để giải quyết vấn đề này, nhiều lớp học cung cấp các phương thức swap riêng của họ (bao gồm std::vector) thay vì trao đổi các con trỏ đến dữ liệu nội bộ của chúng. Điều này hiệu quả hơn và có thể được đảm bảo để không bao giờ thất bại.

Nhưng bây giờ bạn có trường hợp bạn có thể sử dụng std::swap(x, y) trên một số loại nhưng cần phải gọi x.swap(y) trên các loại khác. Điều này là khó hiểu, và nó là xấu cho các mẫu vì họ sẽ không thể trao đổi hai đối tượng một cách thống nhất, nhất quán.

Nhưng std::swap có thể là chuyên để gọi số x.swap(y) khi được gọi trên các loại cụ thể. Điều đó có nghĩa là bạn có thể sử dụng std::swap ở khắp mọi nơi và hy vọng nó sẽ hoạt động tốt.

+3

Một chút hẹp vì tiêu chuẩn rõ ràng cấm quá tải std :: swap trong khi cho phép một cách rõ ràng (để lại các mẫu của riêng bạn trong thời tiết lạnh khi bạn có thể ' t một phần chuyên cho họ). Giải pháp là sử dụng liên tục các hàm hoán đổi không thành viên (ví dụ như được định nghĩa là bạn bè), 'using std :: swap;' using-declaration, và gọi ADL (Argument Dependent Lookup) với 'swap (a, b)' (ghi chú không có "std ::" ở phía trước) --- kết quả này là linh hoạt hơn và chung chung hơn cho C++ hiện tại so với chuyên std :: swap. –

+1

Ah, phải, thực sự chuyên 'std :: swap' cho' std :: vector' theo cách này sẽ yêu cầu một phần chuyên môn hóa. Argh. – jamesdlin

3

Tôi không thể nghĩ ra một ví dụ và tôi đã cố gắng gần như kể từ khi bạn hỏi. Như được chỉ ra bởi Jagannath, nó đã được long-standing advice không chuyên chức năng, nhưng thay vì quá tải chúng hoặc sử dụng một đặc điểm lớp (trong đó có thể là chuyên ngành, thậm chí một phần chuyên ngành).

Ví dụ, nếu bạn cần phải trao đổi hai mặt hàng, sau đó dựa vào quá tải là tốt hơn (dễ dự đoán hơn và mở rộng hơn):

template<class T> 
void f() { 
    T a, b; 
    using std::swap; // brings std::swap into scope as "fallback" 
    swap(a, b); // unqualified call (no "std::") so ADL kicks in 
    // also look at boost::swap 
} 

Và làm thế nào bạn viết một trao đổi với nhiều loại bạn:

// the cleanest way to do it for a class template: 
template<class T> 
struct Ex1 { 
    friend void swap(Ex1& a, Ex1& b) { /* do stuff */ } 
}; 

// you can certainly place it outside of the class instead---but in the 
// same namespace as the class---if you have some coding convention 
// against friends (which is common, but misguided, IMHO): 
struct Ex2 {}; 
void swap(Ex2& a, Ex2& b) { /* do stuff */ } 

Cả hai đều cho phép Argument Dependent Lookup (ADL).

chức năng khác, chẳng hạn như một stringify/str hoặc một repr (đại diện) tương tự có thể không phải là thành viên và tận dụng lợi thế của ADL qua quá tải:

struct Ex3 { 
    friend std::string repr(Ex3 const&) { return "<Ex3 obj>"; } 
}; 

std::string repr(bool b) { return b ? "true" : "false"; } 

// possible fallback: 
template<class T> 
std::string repr(T const& v) { 
    std::ostringstream out; 
    out << v; 
    return out.str(); 
} 
// but in this particular case, I'd remove the fallback and document that 
// repr() must be overloaded appropriately before it can be used with a 
// particular type; for other operations a default fallback makes sense 

Để nhìn vào nó một cách khác, nó sẽ là tốt đẹp nếu các mẫu chức năng có thể phục vụ như là một đăng ký để triển khai cụ thể, nhưng do giới hạn (trong C++ hiện tại, không chắc chắn chính xác những gì C++ 0x mang đến đây) chúng không hoạt động tốt như quá tải hoặc mẫu lớp mục đích đăng ký.

Có một cách sử dụng thuận tiện nhưng không quan trọng: dễ dàng xác định các chuyên môn nhất định trong thư viện riêng, có thể là thư viện được chia sẻ (.so hoặc .dll). Điều này thuận tiện bởi vì nó đòi hỏi những thay đổi tối thiểu cho mẫu chung, nhưng không quan trọng vì nó có vẻ hiếm hoi với tôi (trong tự nhiên, và chắc chắn là hiếm trong kinh nghiệm của tôi) và những người triển khai vẫn có thể sử dụng quá tải hoặc chuyển tiếp đến một lớp hoàn toàn chuyên biệt phương thức không chuyên biệt của mẫu.

+0

Câu trả lời hay.Một điều mặc dù: Tôi nghĩ rằng tuyên bố bạn bè của bạn trong đoạn mã thứ 3 cần cú pháp ID mẫu đầy đủ ('friend std :: string repr (ex3 const &) {...}') phải không? (Và mẫu đó cần phải được xác định hoặc ít nhất được khai báo trước điều này.) –

+0

@j_random: Không, tất cả các mã là hợp lệ và tiêu chuẩn như là; nghĩa là sao chép vào một tệp (sau #includes thích hợp) và biên dịch nó: http://codepad.org/v45tt46L. (Repr đó không phải là một mẫu, btw, nhưng nếu nó được, nó sẽ không cần cú pháp thêm, hoặc, giống như trao đổi của Ex1 không cần nó.) –

+0

Rất tiếc, bằng cách nào đó tôi đã bỏ lỡ rằng bạn đã đưa ra tại chỗ định nghĩa cho 'repr()' - vâng, điều đó hoàn toàn hợp lệ. Nhưng nếu bạn thay vào đó chỉ đưa ra tuyên bố, với định nghĩa mẫu "dự phòng" và chuyên môn thích hợp được khai báo trước, thì tiêu chuẩn yêu cầu (14.5.3) tuyên bố bạn bè của bạn sử dụng id * template * (ví dụ: 'friend std :: string repr (...); ') hoặc * id đủ điều kiện * (ví dụ:' friend std :: string N :: repr (...); 'và đặt mẫu vào không gian tên' N') . Tôi thấy hỗ trợ trình biên dịch rất nghi ngờ trong các bài kiểm tra của tôi không may ... –

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