2011-10-25 4 views
7

Có mã này:mẫu variadic - không đầy đủ loại

template<class ...Args> 
struct Are_Same 
{ 
    enum {value = Are_Same<Args...>::value}; 
}; 

template<class A,class... C> 
struct Are_Same<A,C...> 
{ 
    enum {value = Are_Same<A,C...>::value};//HERE is THE ERROREOUS LINE 
}; 

template<class A,class B> 
struct Are_Same<A,B> 
{ 
    enum {value = std::is_same<A,B>::value}; 
}; 

Tôi nhận được lỗi từ gcc 4.6.1:

error: incomplete type 'Are_Same' used in nested name specifier.

Tôi nghĩ rằng bằng cách làm Are_Same<A,C...>::value tôi sẽ gọi cuộc gọi đệ quy đó có ít cuối cùng sẽ đơn giản mở rộng đến Are_Same<A,B>. Rõ ràng không phải vậy. Có ai biết tôi đang phạm sai lầm ở đâu không?

Trả lời

8

Tôi nghĩ rằng các định nghĩa của các mẫu là sai, trong cả hai trường hợp bạn đang kích hoạt đệ quy chính xác. Tôi dự kiến ​​sẽ có trình biên dịch để chết với một số stackoverflow bên trong trình biên dịch nhưng một lỗi khác nhau được sản xuất ...

An thực hiện các are_same mẫu variadic có thể là:

template <class... Args>     // base (optional to declare the template) 
struct are_same; 

template <class A, class B, class... Args> // recursion 
struct are_same<A,B,Args...> { 
    static const bool value = is_same<A,B>::value && are_same<A,Args...>::value; 
}; 

template <class A, class B>     // stop condition 
struct are_same<A,B> { 
    static const bool value = is_same<A,B>::value; 
}; 

Lưu ý rằng trong bước recursion , một đối số bị xóa khỏi danh sách các đối số, do đó, vấn đề mới cần giải quyết là đã giảm phiên bản gốc. Loại lập trình meta mẫu này khá liên quan đến đệ quy, và các quy tắc tương tự được áp dụng, để có thể sử dụng đệ quy, bạn cần phải đảm bảo rằng mỗi bước đệ quy sẽ đưa bạn đến gần hơn với một giải pháp. Trong trường hợp cụ thể này, với danh sách N kiểu có khả năng tương tự, mỗi bước làm giảm vấn đề để tìm xem các kiểu N-1 có giống nhau hay không.

Bạn có thể sử dụng cách khác, như điều kiện dừng (thay thế một cựu) một phiên bản thoái hóa của vấn đề are_same:

template <class A> 
struct are_same<A> { 
    static const bool value = true; 
}; 

Đó là thoái hóa theo nghĩa là nó không thực sự có ý nghĩa hỏi xem một loại duy nhất * are_same *, nhưng đối với các tác vụ lập trình meta khác nhau, nó có thể phù hợp.

Một thuật toán có khả năng hiệu quả hơn khác nhau (Tôi không chắc liệu trình biên dịch sẽ tránh instantiation của mẫu trong bước đệ quy ở trên) mà không phụ thuộc vào is_same có thể là:

template <class... Args> 
struct are_same; 

template <class A, class... Args> 
struct are_same<A,A,Args...> {    // recursion 
    static const bool value = are_same<A,Args...>::value; 
}; 

template <class A, class B, class... Args> 
struct are_same<A,B,Args...> {    // cut, A and B are not the same 
    static const bool value = false; 
}; 

template <class A> 
struct are_same<A> {      // end of recursion 
    static const bool value = true; 
}; 

Trong trường hợp này , trình biên dịch sẽ thích recursion tới các bước cut bất cứ khi nào hai loại giống nhau, vì vậy chúng tôi không cần kiểm tra is_same nội bộ. Đồng thời, nếu trình biên dịch đi vào bước cut, chúng tôi không cần xử lý phần còn lại của danh sách loại, vì chúng tôi đã biết câu trả lời.

+1

thật là rực rỡ, đặc biệt là bước "ngắn mạch". Cảm ơn. – smallB

+0

Rất hay, nhưng có một lỗi nhỏ trong đoạn mã 1. Nên là 'static const bool value = is_same :: value && are_same :: value;' –

+1

@VJo: Chúng giống hệt nhau, một khi bạn đã chứng minh rằng 'A == B', định kỳ với' 'hoặc' 'không quan trọng chút nào, cũng không có lợi thế gì, bạn đang kích hoạt một phiên bản mới của mẫu và tại thời điểm này trình biên dịch sẽ giải quyết cho bất cứ kiểu' A' và 'B' nào (tức là' int ',' double' ...), cả hai đoạn mã đều giống nhau. –

3

tôi sẽ làm điều đó như thế này:

thực hiện
#include <type_traits> 
#include <iostream> 

template <class... Args> 
struct are_same 
{ 
    static const bool value=true; 
}; 

template <class A, class B, class... Args> // recursion 
struct are_same<A,B,Args...> { 
    static const bool value = std::is_same<A,B>::value && are_same<B,Args...>::value; 
}; 

int main() 
{ 
    std::cout<< std::boolalpha << are_same<int>::value << std::endl; 
    std::cout<< std::boolalpha << are_same< int, int, int >::value << std::endl; 
    std::cout<< std::boolalpha << are_same< int, int, double, int >::value << std::endl; 
} 
+0

cảm ơn tôi thích cách mà không dừng điều kiện. Công cụ tuyệt vời; +1 – smallB

+2

@smallB: Điều này chứa chính xác điều kiện dừng giống nhau, chỉ ẩn trong mẫu chung, chỉ có hai chuyên môn, một cho bước chung, một cho điều kiện dừng (trong trường hợp này điều kiện dừng trông * nhiều hơn chung * vì nó là 'template ' nhưng nó sẽ chỉ là đối sánh tốt nhất cho trình biên dịch khi nó không thể áp dụng bước 'recursion' (nghĩa là khi có một kiểu đơn) Trong trường hợp của tôi thích làm cho điều kiện dừng rõ ràng hơn khi tôi ghét phải suy nghĩ và phải tìm ra cách/khi đệ quy sẽ dừng lại, nhưng đó chỉ là ưu tiên. –

0

Có lẽ đơn giản nhất có thể là như thế này:

template <typename... TList> 
struct are_same { constexpr static bool value = false; }; 

template <typename T, typename... TList> 
struct are_same<T, T, TList...> { 
    constexpr static bool value = are_same<T, TList...>::value; 
}; 

template <typename T> 
struct are_same<T> { constexpr static bool value = true; }; 

Hoặc bạn có thể thay thế điều kiện dừng với

template <typename T> 
struct are_same<T, T> { constexpr static bool value = true; }; 

Nhưng người đầu tiên một cái tổng quát hơn vì are_same<type>::value == true. Một câu hỏi khác là are_same<>::value bằng. Điều này cung cấp cho bạn false nhưng nó không phải là một việc lớn để thêm một chuyên môn mẫu như thế này.

template <> 
struct are_same<> { constexpr static bool value = true; };