2010-06-09 28 views
15

Tôi đang cố gắng viết mã sử dụng thành phần typedef của đối số mẫu, nhưng muốn cung cấp loại mặc định nếu đối số mẫu không có typedef đó. Một ví dụ đơn giản mà tôi đã thử là:Chuyên môn mẫu để sử dụng loại mặc định nếu thành viên nhóm typedef không tồn tại

struct DefaultType { DefaultType() { printf("Default "); } }; 
struct NonDefaultType { NonDefaultType() { printf("NonDefault "); } }; 

struct A {}; 
struct B { typedef NonDefaultType Type; }; 

template<typename T, typename Enable = void> struct Get_Type { 
    typedef DefaultType Type; 
}; 
template<typename T> struct Get_Type< T, typename T::Type > { 
    typedef typename T::Type Type; 
}; 

int main() 
{ 
    Get_Type<A>::Type test1; 
    Get_Type<B>::Type test2; 
} 

Tôi hy vọng điều này sẽ in "Mặc định không mặc định", nhưng thay vào đó sẽ in "Mặc định mặc định". Kỳ vọng của tôi là dòng thứ hai trong main() phải khớp với phiên bản đặc biệt của Get_Type, vì B :: Type tồn tại. Tuy nhiên, điều này không xảy ra.

Có ai có thể giải thích những gì đang diễn ra ở đây và cách khắc phục hoặc cách khác để đạt được mục tiêu tương tự?

Cảm ơn bạn.

Chỉnh sửa:

Georg đã đưa ra một phương pháp thay thế, nhưng tôi vẫn tò mò về lý do tại sao điều này không hiệu quả. Theo các các tăng enable_if tài liệu, một cách để chuyên một mẫu với nhiều loại khác nhau là như vậy:

template <class T, class Enable = void> 
class A { ... }; 

template <class T> 
class A<T, typename enable_if<is_integral<T> >::type> { ... }; 

template <class T> 
class A<T, typename enable_if<is_float<T> >::type> { ... }; 

này hoạt động vì enable_if < đúng> đã gõ như một typedef, nhưng enable_if < false> không.

Tôi không hiểu điều này khác với phiên bản của tôi như thế nào, thay vì sử dụng enable_if tôi chỉ sử dụng T :: Loại trực tiếp. Nếu T :: Loại tồn tại sẽ không giống như enable_if < true> :: nhập vào ví dụ trên và khiến chuyên môn được chọn? Và nếu T :: Type không tồn tại, nó sẽ không giống như enable_if < false> :: type không tồn tại và gây ra phiên bản mặc định được chọn trong ví dụ trên?

+0

Uh .. mục tiêu là gì? –

+0

Mục tiêu là Get_Type :: Loại sẽ là T :: Nhập nếu nó tồn tại hoặc DefaultType nếu nó không tồn tại. – Frank

Trả lời

8

Để trả lời bổ sung của bạn - đối số chuyên môn của bạn chuyển thành viên typedef và hy vọng nó mang lại void làm loại. Không có gì kỳ diệu về điều này - nó chỉ sử dụng một đối số mặc định. Hãy xem cách nó hoạt động. Nếu bạn nói Get_Type<Foo>::type, trình biên dịch sẽ sử dụng đối số mặc định là Enable, là void và tên loại sẽ trở thành Get_Type<Foo, void>::type. Bây giờ, trình biên dịch kiểm tra xem có bất kỳ một chuyên môn nào phù hợp.

Danh sách đối số một phần của chuyên môn của bạn <T, typename T::Type> được suy ra từ danh sách đối số gốc <Foo, void>. Điều này sẽ suy ra T thành Foo và sau đó thay thế Foo thành đối số thứ hai của chuyên môn, cho kết quả cuối cùng là <Foo, NonDefaultType> để chuyên môn hóa một phần của bạn. Tuy nhiên, điều đó không khớp với danh sách đối số gốc <Foo, void>!

Bạn cần một cách để mang lại kiểu void, như trong những điều sau đây:

template<typename T> 
struct tovoid { typedef void type; }; 

template<typename T, typename Enable = void> struct Get_Type { 
    typedef DefaultType Type; 
}; 
template<typename T> 
struct Get_Type< T, typename tovoid<typename T::Type>::type > { 
    typedef typename T::Type Type; 
}; 

Bây giờ điều này sẽ làm việc như bạn mong đợi. Sử dụng MPL, bạn có thể sử dụng always thay vì tovoid

typename apply< always<void>, typename T::type >::type 
+0

Cảm ơn Johannes. Tôi đã không nhận ra rằng chính bản thân nó không có giá trị để phù hợp, nhưng điều đó có ý nghĩa hoàn hảo. – Frank

7

Bạn có thể làm điều đó bằng cách sử dụng SFINAE:

template<class T> struct has_type { 
    template<class U> static char (&test(typename U::Type const*))[1]; 
    template<class U> static char (&test(...))[2]; 
    static const bool value = (sizeof(test<T>(0)) == 1); 
}; 

template<class T, bool has = has_type<T>::value> struct Get_Type { 
    typedef DefaultType Type; 
}; 

template<class T> struct Get_Type<T, true> { 
    typedef typename T::Type Type; 
}; 
+0

Cảm ơn George, điều đó có hiệu quả. Tôi đã chỉnh sửa câu hỏi của mình với một câu hỏi khác về lý do tại sao triển khai của tôi không hoạt động, bởi vì nó có vẻ giống như nguyên tắc mà chính enable_if dựa trên đó. enable_if có typedef, nhưng enable_if thì không. Tôi không hiểu tại sao điều đó không giống như việc T :: Type tồn tại hay không. – Frank

+0

@Frank: Johannes đã trả lời tốt nhất trong câu trả lời khác. –

4

Bước đầu tiên: ngừng sử dụng "Type" và sử dụng các tiêu chuẩn mpl "loại".


BOOST_MPL_HAS_XXX_DEF(Type) 

template < typename T > 
struct get_type { typedef typename T::Type type; }; 

template < typename T > 
struct calculate_type : boost::mpl::if_ 
< 
    has_Type<T> 
, get_type<T> 
, boost::mpl::identity<default_type> 
>::type {} 

typedef calculate_type<A>::type whatever; 

Nếu bạn sử dụng "loại" thay vì "Loại" trong metafunctions của bạn, bạn sẽ không yêu cầu "get_type" fetcher để chuyển đổi nó và chỉ có thể trở lại T trong trường hợp đó.

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