2012-05-15 43 views
6

Vấn đề này hơi khó giải thích, vì vậy tôi sẽ bắt đầu bằng ví dụ:Chuyên môn về mẫu cho các lớp con của lớp cơ sở mẫu

Tôi có một loại lớp và hằng số nguyên như tham số mẫu và tôi có một số lớp trẻ mà xuất phát từ sự khởi tạo của mẫu mà:

template <class V, int i> 
struct Base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

struct Child : public Base<int,12> 
{ 
}; 

tôi muốn sử dụng các lớp này với một số mẫu khác (chúng ta hãy gọi nó Test), trong đó có chuyên ngành với nhiều loại khác nhau. Bởi vì hành vi phải giống hệt nhau đối với tất cả các lớp có nguồn gốc từ bất kỳ sự khởi tạo nào của Base, tôi muốn định nghĩa chỉ một chuyên môn duy nhất của Test để xử lý tất cả các lớp bắt nguồn từ Base.

Tôi biết rằng tôi không thể trực tiếp chuyên cho Base < V, i > vì điều này sẽ không phát hiện các lớp học trẻ em. Thay vào đó, cách tiếp cận đầu tiên của tôi đã sử dụng enable_if và loại đặc điểm Boost của:

// empty body to trigger compiler error for unsupported types 
template <class T, class Enabled = void> 
struct Test { }; 

// specialization for ints, 
// in my actual code, I have many more specializations here 
template <class Enabled> 
struct Test <int, Enabled> 
{ 
    static void test (int dst) 
    { 
     cout << "Test<int>::test(" << dst << ")" << endl; 
    } 
}; 

// this should handle all subclasses of Base, 
// but it doesn't compile 
template <class T, class V, int i> 
struct Test <T, typename enable_if <is_base_and_derived <Base <V,i>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

int main (int argc, char **argv) 
{ 
    Test <int>::test (23); 
    Test <Child>::test (Child()); 
    return 0; 
} 

Ý tưởng là các chuyên môn hóa nên xử lý tất cả các lớp học mà có nguồn gốc từ cơ sở với bất kỳ giá trị độc đoán của V và i. Điều này không làm việc, gcc phàn nàn:

 
error: template parameters not used in partial specialization: 
error:   ‘V’ 
error:   ‘i’ 

Tôi đoán vấn đề là phương pháp này sẽ yêu cầu trình biên dịch để thử tất cả các kết hợp có thể có của V và tôi để kiểm tra xem ai trong số họ phù hợp. Còn bây giờ, tôi làm việc xung quanh vấn đề bằng cách thêm một cái gì đó để các lớp cơ sở:

template <class V, int i> 
struct Base 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

Bằng cách này, chuyên môn hóa không còn cần phải có V và i như miễn phí thông số mẫu:

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

Và sau đó nó biên dịch.

Bây giờ, câu hỏi của tôi là: Làm cách nào tôi có thể thực hiện việc này mà không sửa đổi lớp cơ sở? Trong trường hợp này, có thể vì tôi đã tự viết, nhưng tôi có thể làm gì nếu tôi phải xử lý mã thư viện của bên thứ ba trong mẫu Thử nghiệm của tôi như thế? Có một giải pháp thanh lịch hơn không?

Chỉnh sửa: Ngoài ra, ai đó có thể cho tôi giải thích chi tiết lý do chính xác cách tiếp cận đầu tiên không hoạt động? Tôi có một ý tưởng thô, nhưng tôi muốn có một sự hiểu biết đúng đắn. :-)

Trả lời

3

một giải pháp đơn giản là để cho Base thừa hưởng khác Base_base:

struct Base_base 
{}; 

template <class V, int i> 
struct Base 
: public Base_base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base_base, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

[Edited] trong một mã bên thứ 3, bạn có thể sử dụng một thủ thuật như:

template <class V, int i> 
struct Base3rdparty 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class V, int i> 
struct Base 
: public Base3rdparty<V, i> 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 
+0

Cảm ơn mẹo Base_Base, điều này làm cho mã hiện tại của tôi dễ đọc hơn một chút. Tuy nhiên, đối với thư viện của bên thứ ba, điều này sẽ không hoạt động, ít nhất là nếu các lớp con cũng thuộc về thư viện. –

+1

@BenjaminSchug: có lẽ [this] (http://stackoverflow.com/a/6398983/1324131) trả lời câu hỏi mới của bạn trong câu hỏi được chỉnh sửa. – user2k5

+0

Cảm ơn, điều này giải thích tại sao phương pháp đầu tiên không hoạt động.Có vẻ như cảm giác ruột của tôi là chính xác. –

0

Sử dụng chức năng quá tải và decltype:

// Never defined: 
template<typename T> std::false_type is_Base(T&); 
template<class V, int I> std::true_type is_Base(Base<V,I>&); 

template<typename IsBase, typename T> struct TestHelper; 
template<typename T> struct TestHelper<std::true_type, T> 
{ 
    static void test(const T& dst) { dst.doSomething(); } 
}; 
template<> struct TestHelper<std::false_type, int> 
{ 
    static void test(int dst) 
    { std::cout << "Test<int>::test(" << dst << ")" << std::endl; } 
}; 
// ... 

template<typename T> struct Test 
{ 
    static void test(const T& dst) 
    { TestHelper<decltype(is_Base(std::declval<T&>())), T>::test(dst); } 
} 
Các vấn đề liên quan