2010-06-21 35 views
18

Tôi không hiểu tại sao đoạn mã này (from Wikipedia) công trình:tính giai thừa với các mẫu thiết meta-lập trình

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

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

// Factorial<4>::value == 24 
// Factorial<0>::value == 1 
void foo() 
{ 
    int x = Factorial<4>::value; // == 24 
    int y = Factorial<0>::value; // == 1 
} 
  • mẫu kỳ lạ này mà mất <int N> là gì?
  • Điều này là thứ hai mẫu lạ <>?
  • Cấu trúc enum là gì?
  • Lợi thế của việc sử dụng tính toán giai thừa thời gian chạy thay vì bình thường này là gì?
  • Bạn có thường xuyên sử dụng điều này không? Tôi đã sử dụng C++ trong một thời gian, nhưng chưa bao giờ sử dụng nó trước đây. Làm thế nào lớn một phần của C + + là tôi bỏ lỡ?

Cảm ơn!

+1

http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list – GManNickG

Trả lời

21
  • Mẫu lạ này mất <int N> là gì?

Trong C++, lập luận mẫu có thể là các loại (bắt đầu bằng class hoặc typename) hoặc số nguyên (bắt đầu bằng int hay unsigned int). Ở đây chúng ta đang ở trong trường hợp thứ hai.

  • Điều kỳ lạ thứ hai template <> là gì?

template<> struct Factorial<0> là một chuyên môn hóa hoàn toàn thừa lớp mẫu, có nghĩa là 0 được coi là một giá trị đặc biệt để tương ứng với phiên bản riêng của mình thừa.

  • Enums for?

enums là cách để tính giá trị trong lập trình meta C++

  • lợi thế của việc sử dụng này chứ không phải là bình thường runtime tính giai thừa là gì?

Lý do tại sao mã này được tạo ở nơi đầu tiên là tạo bằng chứng khái niệm có thể thực hiện bằng cách sử dụng lập trình meta. Ưu điểm là mã được tạo là cực kỳ hiệu quả (gọi Factorial<4>::value tương đương với chỉ đơn giản là viết "24" trong mã của bạn.

  • Bạn thường người ta sử dụng này? Tôi đã được sử dụng C++ cho một thời bây giờ, nhưng không bao giờ sử dụng này trước đây. Làm thế nào lớn là một phần của C++ được tôi bỏ lỡ?

chức năng như vậy hiếm khi đạt được bằng phương pháp này, nhưng lập trình meta được sử dụng nhiều và ngày nay hơn. Xem Boost meta-programming library để có được một gợi ý những gì có thể được thực hiện.

+8

Các tham số mẫu không kiểu không giới hạn ở số nguyên. Chúng có thể là bất kỳ kiểu tích phân hoặc liệt kê nào, con trỏ tới đối tượng hoặc con trỏ đến hàm, tham chiếu đến đối tượng hoặc tham chiếu đến hàm hoặc con trỏ tới thành viên. –

+1

@ Benoît: Tôi không biết nhiều về các mẫu, nhưng không nên là 'template <0> struct Factorial' thay vì' template <> struct Factorial <0> 'trong trường hợp thứ hai? – Lazer

+0

@ Benoît: Ngoài ra, việc sử dụng 'enum' theo cách nó được sử dụng là gì? – Lazer

1

Đó là đệ quy được thực hiện bởi trình biên dịch, nơi

template <> 
struct Factorial<0> 
{ 
} 

là điểm thoát của đệ quy.

Lúc đầu, Factorial<4> được giải quyết. Không có chuyên môn của Factorial cho giá trị 4, do đó, định nghĩa đầu tiên Factorial<> được sử dụng. Giá trị của nó sau đó được tính bằng Factorial<3>::value, trong đó bước lặp lại trước đó được lặp lại.

Điều này sẽ tiếp tục, cho đến N == 1, sau đó cho N-1, chuyên môn Factorial<0> đi vào hoạt động, trong đó giá trị được đặt thành 1. Phép đệ quy dừng lại ở đây và trình biên dịch có thể đi lên và tính toán phần còn lại giá trị của Factorial<N>.

2

Lợi thế của việc sử dụng tính toán giai thừa thời gian chạy thay vì bình thường này là gì?

Nó nhanh hơn - về mặt lý thuyết trình biên dịch sẽ mở rộng tập hợp mẫu tại thời điểm biên dịch sao cho Factorial<n>::value đơn giản là một hằng số. Trong một số ít trường hợp biến mất, điều đó có thể tạo nên sự khác biệt.

Mặt trái của nó là nó phải được tính toán tại thời gian biên dịch, vì vậy nếu n của bạn được xác định khi chạy thì bạn không thể sử dụng phương thức đó.

Bạn có thường xuyên sử dụng điều này không? Tôi đã sử dụng C++ trong một thời gian, nhưng chưa bao giờ sử dụng trước đây. Làm thế nào lớn một phần của C + + là tôi bỏ lỡ?

Trường hợp như vậy, không thường xuyên. Lập trình meta mẫu có khả năng rất mạnh, nhưng số lượng các trường hợp mà ví dụ này hữu ích và đủ quan trọng để thực hiện là rất nhỏ.

Nhưng nó là một kỹ thuật hữu ích trong C++ vì nó cho phép ngôn ngữ thực hiện rất nhiều thủ thuật mạnh mẽ mà nếu không sẽ không thể tiếp cận được. Tôi muốn mạo hiểm rằng nó phổ biến hơn nhiều để tận dụng lợi thế của lập trình meta mẫu mà người khác đã làm cho bạn - ví dụ. Boost sử dụng nó khá nhiều và có thể làm một số điều rất thông minh. Nó mạnh mẽ, nhưng cũng có thể làm xáo trộn mã nếu không được thực hiện tốt.

6

Mẫu lạ này mất <int N> là gì?

Tuyên bố mẫu có thể được thực hiện trên các lớp/loại, về giá trị và trên con trỏ. Bạn thường không thấy biểu mẫu này khi xác định mẫu hiếm khi sử dụng hằng số trong các tham số mẫu.

Mẫu lạ thứ hai này <>?

Cách tốt nhất để nghĩ về mẫu là làm điều gì đó tương tự như trình biên dịch thực hiện: xem xét mẫu làm lớp, nơi bạn thay thế tham số mẫu bằng giá trị thực của loại đó.

Đó là, đối với:

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

Factorial<2>::value tương đương với:

// generated by the compiler when Factorial<2>::value is encountered in code: 
struct Factorial2 { enum { value = 2 * Factorial1::value }; }; 
struct Factorial1 { enum { value = 1 * Factorial0::value }; }; 
// not generated by compiler, as it was explicitly defined in the code you ask about: 
template <> struct Factorial<0> { enum { value = 1 }; }; // defines Factorial<0> 

các enums là cái gì?

Chúng xác định một điều tra với một const value tại thời gian biên dịch, cho phép bạn viết mã máy khách như mã trong ví dụ của bạn.

Lợi thế của việc sử dụng số này thay vì tính toán thời gian chạy bình thường là gì?

Lợi thế là hiệu quả. Vì tính toán giá trị được mở rộng khi biên dịch, chi phí thời gian chạy là Factorial<10>::value giống như giá trị Factorial < 1> ::. Trong mã được biên dịch, chúng là cả hai hằng số. Nếu bạn tính toán giai thừa trong mã quan trọng hiệu suất và bạn biết thời gian biên dịch có giá trị như thế nào, bạn nên làm điều này hoặc tính toán ngoại tuyến và xác định hằng số với giá trị được tính toán trước trong mã của bạn.

Bạn có thường xuyên sử dụng điều này không?

Có phải "điều này" bạn đề cập đến tính toán đệ quy của một hằng số không? Nếu có, không thường xuyên.

Định nghĩa hằng số trong mẫu có phải là "điều này" không? Rất thường xuyên :)

Đây có phải là "điều này" đặc trưng của mẫu cho một loại cụ thể không? Rất Rất thường xuyên. Tôi hiểu rằng std :: vector có một thực hiện hoàn toàn riêng biệt trong một số triển khai STL (một std::vector<bool> với 8 yếu tố không nên cần nhiều hơn 1 byte để lưu trữ các giá trị).

Tôi đã sử dụng C++ một lúc rồi, nhưng chưa bao giờ sử dụng trước đây. Làm thế nào lớn một phần của C + + là tôi bỏ lỡ?

Không nhất thiết phải là một phần lớn. Nếu bạn sử dụng boost, có khả năng mã bạn sử dụng được thực hiện bằng cách sử dụng những thứ như thế này.

3

Tôi tìm thấy this answer bởi Johannes Schaub - litb trên một câu hỏi khác rất hữu ích.

[Tôi đang sao chép dán câu trả lời ở đây, giả sử Johannes không sao với nó.Tôi sẽ xóa hình dán nếu anh ấy không thích nó]


Vâng, đó là thông số không phải loại. Bạn có thể có nhiều loại thông số mẫu

  • Tham số loại.
    • loại
    • Templates (chỉ các lớp học, không có chức năng)
  • Non-type Parameters
    • Pointers
    • Tài liệu tham khảo
    • Integral biểu thức hằng số

Những gì bạn có ở đây là loại cuối cùng. Đó là hằng số thời gian biên dịch (được gọi là biểu thức hằng số) và là kiểu số nguyên hoặc kiểu liệt kê. Sau khi tìm kiếm nó trong tiêu chuẩn, tôi đã phải di chuyển các mẫu lớp lên vào phần loại - mặc dù các mẫu không phải là loại. Tuy nhiên, chúng được gọi là kiểu tham số cho mục đích mô tả những loại đó. Bạn có thể có con trỏ (và cũng là con trỏ thành viên) và tham chiếu đến các đối tượng/hàm có liên kết bên ngoài (những liên kết có thể được liên kết từ các tệp đối tượng khác và địa chỉ của nó là duy nhất trong toàn bộ chương trình). Ví dụ:

Template loại tham số:

template<typename T> 
struct Container { 
    T t; 
}; 

// pass type "long" as argument. 
Container<long> test; 

Mẫu số nguyên tham số:

template<unsigned int S> 
struct Vector { 
    unsigned char bytes[S]; 
}; 

// pass 3 as argument. 
Vector<3> test; 

tham số con trỏ Template (đi qua một con trỏ tới một hàm)

template<void (*F)()> 
struct FunctionWrapper { 
    static void call_it() { F(); } 
}; 

// pass address of function do_it as argument. 
void do_it() { } 
FunctionWrapper<&do_it> test; 

tham số mẫu tham khảo (chuyển số nguyên)

template<int &A> 
struct SillyExample { 
    static void do_it() { A = 10; } 
}; 

// pass flag as argument 
int flag; 
SillyExample<flag> test; 

Tham số mẫu mẫu.

template<template<typename T> class AllocatePolicy> 
struct Pool { 
    void allocate(size_t n) { 
     int *p = AllocatePolicy<int>::allocate(n); 
    } 
}; 

// pass the template "allocator" as argument. 
template<typename T> 
struct allocator { static T * allocate(size_t n) { return 0; } }; 
Pool<allocator> test; 

Mẫu không có tham số nào là không thể. Tuy nhiên, một mẫu mà không cần bất kỳ lý lẽ rõ ràng là có thể - nó có đối số mặc định:

template<unsigned int SIZE = 3> 
struct Vector { 
    unsigned char buffer[SIZE]; 
}; 

Vector<> test; 

Cú pháp, template<> được dành riêng để đánh dấu một mẫu chuyên môn rõ ràng, thay vì một mẫu không có tham số:

template<> 
struct Vector<3> { 
    // alternative definition for SIZE == 3 
}; 

+1

Nếu bạn muốn liên kết đến câu trả lời có liên quan, hãy sử dụng các nhận xét cho điều đó. Không chỉ sao chép/dán câu trả lời đầy đủ mà không có bất kỳ bổ sung của chính mình. –

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