2008-11-11 36 views
21

Tôi muốn thực hiện chính việc thay đổi chính này. Có thể chuyên gì đó dựa trên lớp cơ sở của nó không? Tôi cũng mong là như vậy.Chuyên môn về mẫu dựa trên lớp kế thừa

-edit-

Tôi sẽ có một số lớp kế thừa từ SomeTag. Tôi không muốn viết cùng chuyên môn cho mỗi người.

class SomeTag {}; 
class InheritSomeTag : public SomeTag {}; 

template <class T, class Tag=T> 
struct MyClass 
{ 
}; 

template <class T> 
struct MyClass<T, SomeTag> 
{ 
    typedef int isSpecialized; 
}; 

int main() 
{ 
    MyClass<SomeTag>::isSpecialized test1; //ok 
    MyClass<InheritSomeTag>::isSpecialized test2; //how do i make this specialized w/o changing main() 
    return 0; 
} 

Trả lời

23

Bài viết này mô tả một mẹo nhỏ gọn: http://www.gotw.ca/publications/mxc++-item-4.htm

Đây là ý tưởng cơ bản. Trước tiên, bạn cần một lớp IsDerivedFrom (điều này cung cấp thời gian chạy và biên dịch kiểm tra thời gian):

template<typename D, typename B> 
class IsDerivedFrom 
{ 
    class No { }; 
    class Yes { No no[3]; }; 

    static Yes Test(B*); // not defined 
    static No Test(...); // not defined 

    static void Constraints(D* p) { B* pb = p; pb = p; } 

public: 
    enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) }; 

    IsDerivedFrom() { void(*p)(D*) = Constraints; } 
}; 

Sau đó MyClass của bạn cần một thực hiện đó là có khả năng chuyên:

template<typename T, int> 
class MyClassImpl 
{ 
    // general case: T is not derived from SomeTag 
}; 

template<typename T> 
class MyClassImpl<T, 1> 
{ 
    // T is derived from SomeTag 
    public: 
    typedef int isSpecialized; 
}; 

và MyClass thực sự trông như:

template<typename T> 
class MyClass: public MyClassImpl<T, IsDerivedFrom<T, SomeTag>::Is> 
{ 
}; 

Sau đó, chính của bạn sẽ ổn như trước:

int main() 
{ 
    MyClass<SomeTag>::isSpecialized test1; //ok 
    MyClass<InheritSomeTag>::isSpecialized test2; //ok also 
    return 0; 
} 
+0

Tâm lởn vởn! Tôi đoán điểm của hàm 'No' không xác định là ..... – John

+0

Đẹp. Tôi chỉ có một câu hỏi đơn giản, thực sự ngu ngốc - có một lý do nào đó mà 'class Yes' được khai báo là' No no [3] 'thay vì, nói, 'No no [2]'? Tôi cho rằng điều đó cũng có hiệu quả nhưng tôi có thể thiếu một cái gì đó quan trọng ... –

3

Trong trường hợp của bạn, cách duy nhất mà tôi thấy sẽ được chuyên rõ ràng MyClass cho InheritSomeTag. Tuy nhiên, SeqAn paper đề xuất một cơ chế được gọi là "phân loại mẫu" thực hiện những gì bạn muốn - mặc dù với cú pháp thừa kế khác, do đó mã không tương thích với hàm main hiện tại của bạn.

// Base class 
template <typename TSpec = void> 
class SomeTag { }; 

// Type tag, NOT part of the inheritance chain 
template <typename TSpec = void> 
struct InheritSomeTag { }; 

// Derived class, uses type tag 
template <typename TSpec> 
class SomeTag<InheritSomeTag<TSpec> > : public SomeTag<void> { }; 

template <class T, class Tag=T> 
struct MyClass { }; 

template <class T, typename TSpec> 
struct MyClass<T, SomeTag<TSpec> > 
{ 
    typedef int isSpecialized; 
}; 

int main() 
{ 
    MyClass<SomeTag<> >::isSpecialized test1; //ok 
    MyClass<SomeTag<InheritSomeTag<> > >::isSpecialized test2; //ok 
} 

Điều này có vẻ lạ và rất cồng kềnh nhưng nó cho phép cơ chế thừa kế thực sự có chức năng đa hình được thực hiện tại thời gian biên dịch. Nếu bạn muốn thấy điều này trong hành động, hãy xem một số SeqAn examples.

đó đang được nói, tôi tin rằng SeqAn là một trường hợp đặc biệt và không nhiều ứng dụng sẽ hưởng lợi từ cú pháp cực kỳ khó khăn này (giải mã lỗi biên dịch SeqAn liên quan đến là một thực tại đau * ss!)

19

Vâng, bài viết trong câu trả lời ở trên xuất hiện trong tháng 2 năm 2002. Trong khi nó hoạt động, hôm nay chúng ta biết có những cách tốt hơn. Ngoài ra, bạn có thể sử dụng enable_if:

template<bool C, typename T = void> 
struct enable_if { 
    typedef T type; 
}; 

template<typename T> 
struct enable_if<false, T> { }; 

template<typename, typename> 
struct is_same { 
    static bool const value = false; 
}; 

template<typename A> 
struct is_same<A, A> { 
    static bool const value = true; 
}; 

template<typename B, typename D>         
struct is_base_of {              
    static D * create_d();      
    static char (& chk(B *))[1]; 
    static char (& chk(...))[2];   
    static bool const value = sizeof chk(create_d()) == 1 && 
           !is_same<B volatile const, 
             void volatile const>::value; 
}; 

struct SomeTag { }; 
struct InheritSomeTag : SomeTag { }; 

template<typename T, typename = void> 
struct MyClass { /* T not derived from SomeTag */ }; 

template<typename T> 
struct MyClass<T, typename enable_if<is_base_of<SomeTag, T>::value>::type> { 
    typedef int isSpecialized; 
}; 

int main() { 
    MyClass<SomeTag>::isSpecialized test1;  /* ok */ 
    MyClass<InheritSomeTag>::isSpecialized test2; /* ok */ 
} 
+1

Con người, tôi hoàn toàn quên mất SFINAE khi tôi viết câu trả lời của mình. Tuy nhiên, tôi cho phép nó đứng để hiển thị một thay thế kỳ lạ. –

+0

@litb: Mặc dù, việc xác định các cấu trúc util 'enable_if' và' is_base_of' là thừa nếu có thể sử dụng TR1; nhưng cảm ơn vì đã làm như vậy ở đây, đã giúp tôi hiểu :) – legends2k

+0

'boost' cũng có chức năng này được xây dựng trong: [enable_if] (http://www.boost.org/doc/libs/1_47_0/libs/utility/enable_if.html) và [is_base_of] (http://www.boost.org/doc/libs/1_41_0/libs/type_traits/doc/html/boost_typetraits/reference/is_base_of.html).(Điều này rất hữu ích cho tôi để tìm hiểu vì tôi đã sử dụng gói tăng cường của mình, có thể nó cũng sẽ hữu ích cho người khác). –

14

Và phiên bản ngắn bây giờ, năm 2014, sử dụng C++ - 11:

#include <type_traits> 

struct SomeTag { }; 
struct InheritSomeTag : SomeTag { }; 

template<typename T, bool = std::is_base_of<SomeTag, T>::value> 
struct MyClass { }; 

template<typename T> 
struct MyClass<T, true> { 
    typedef int isSpecialized; 
}; 

int main() { 
    MyClass<SomeTag>::isSpecialized test1;  /* ok */ 
    MyClass<InheritSomeTag>::isSpecialized test2; /* ok */ 
} 
Các vấn đề liên quan