5

Lưu ý: Tôi hiểu rằng phần lớn những gì tôi đang làm ở đây sẽ dễ dàng hơn trong C++ 11, nhưng tôi không thể sử dụng nó trong dự án của mình.Tại sao chuyên môn mẫu của tôi được biên dịch nếu nó không được thực thi?

Tôi đang tạo hệ thống quản lý nội dung. Các yêu cầu cơ bản là:

  1. Người ta phải có thể xác định các lớp "chủ sở hữu nội dung" chứa số lượng véc tơ tùy ý, mỗi giá trị chứa các loại giá trị khác nhau. Ví dụ. IntHolder có thể giữ vector<int>, FloatAndBoolHolder có thể giữ vector<float>vector<bool>, v.v.
  2. Lớp trình giữ nội dung phải có phương thức get<>(). Đối số mẫu cho get<>() là một loại. Nếu chủ sở hữu nội dung có vectơ với loại đó thì get<>() phải trả lại giá trị từ vectơ đó, nếu không cuộc gọi đến get<>() phải tạo ra lỗi trình biên dịch. Ví dụ. nếu tôi có đối tượng IntHolder, thì gọi số get<int>() trên đó sẽ trả về một số int từ véc tơ của nó, nhưng gọi get<float>() trên đó sẽ tạo ra lỗi trình biên dịch.

Tôi đã tìm ra giải pháp thực hiện tất cả điều này. Cảnh báo, mẫu đệ quy trước:

#include <iostream> 
#include <vector> 
#include <string> 
using namespace std; 

int value = 'A'; 


// helper struct that saves us from partially specialized method overloads 
template < class RequestedType, class ActualType, class TContentHolder > 
struct Getter; 


// holds a vector of type TContent, recursively inherits from holders of other types 
template < class TContent, class TAddContentHolders > 
class ContentHolder : public ContentHolder< typename TAddContentHolders::ContentType, typename TAddContentHolders::AdditionalContentTypes > 
{ 
public: 
    typedef TContent ContentType; 
    typedef TAddContentHolders AdditionalContentTypes; 

private: 
    typedef ContentHolder< typename TAddContentHolders::ContentType, typename TAddContentHolders::AdditionalContentTypes > ParentType; 

public: 
    vector<ContentType> mVector; 

    ContentHolder() 
    { 
     for (int i = 0; i < 5; ++i) 
     { 
      mVector.push_back(ContentType(value++)); 
     } 
    } 

    virtual ~ContentHolder() {} 

    template < class RequestedType > 
    RequestedType get() 
    { 
     return Getter< RequestedType, ContentType, ContentHolder < TContent, TAddContentHolders > >::get(this); 
    } 
}; 

// specialization for ending the recursion 
template < class TContent > 
class ContentHolder< TContent, bool > 
{ 
public: 
    typedef TContent ContentType; 
    typedef bool AdditionalContentTypes; 

    vector<ContentType> mVector; 

    ContentHolder() 
    { 
     for (int i = 0; i < 5; ++i) 
     { 
      mVector.push_back(ContentType(value++)); 
     } 
    } 

    virtual ~ContentHolder() {} 

    template < class RequestedType > 
    RequestedType get() 
    { 
     return Getter< RequestedType, ContentType, ContentHolder< ContentType, bool > >::get(this); 
    } 
}; 


// default getter: forwards call to parent type 
template < class RequestedType, class ActualType, class TContentHolder > 
struct Getter 
{ 
    static RequestedType get(TContentHolder* holder) 
    { 
     cout << "getter 1" << endl; 
     return Getter< RequestedType, typename TContentHolder::ContentType, typename TContentHolder::AdditionalContentTypes >::get(holder); 
    } 
}; 

// specialized getter for when RequestedType matches ActualType: return value from holder 
template < class RequestedType, class TContentHolder > 
struct Getter< RequestedType, RequestedType, TContentHolder > 
{ 
    static RequestedType get(TContentHolder* holder) 
    { 
     cout << "getter 2" << endl; 
     return holder->mVector[0]; 
    } 
}; 

// specialized getter for end of recursion 
template < class RequestedType > 
struct Getter< RequestedType, RequestedType, bool > 
{ 
    static RequestedType get(ContentHolder< RequestedType, bool >* holder) 
    { 
     cout << "getter 3" << endl; 
     return holder->mVector[0]; 
    } 
}; 

Đây là cách bạn sử dụng nó:

// excuse the ugly syntax 
class MyHolder : public ContentHolder< int, ContentHolder< bool, ContentHolder< char, bool > > > 
{ 
}; 

int main() { 
    MyHolder h; 
    cout << h.get<int>() << endl; // prints an int 
    cout << h.get<bool>() << endl; // prints a bool 
    cout << h.get<char>() << endl; // prints a char 
    //cout << h.get<float>() << endl; // compiler error 
    return 0; 
} 

Đây là tất cả tiền phạt và dandy, và đáp ứng tất cả các yêu cầu nêu trên. Tuy nhiên, lỗi biên dịch cho get<float>() thực sự là xấu xí. Vì vậy, tôi đã cố gắng để giới thiệu chuyên môn khác cho Getter đó giải thích cho trường hợp khi chúng ta đạt đến kết thúc của hệ thống phân cấp lớp và vẫn chưa tìm thấy một loại phù hợp:

// static assert helper 
template <bool b> 
struct StaticAssert {}; 

template <> 
struct StaticAssert<true> 
{ 
    static void test(const string& s) {} 
}; 


template < class RequestedType, class NonMatchingType > 
struct Getter< RequestedType, NonMatchingType, bool > 
{ 
    static RequestedType get(ContentHolder< NonMatchingType, bool >* holder) 
    { 
     cout << "getter 4" << endl; 
     StaticAssert<false>::test("Type not in list"); 
     return 0; 
    } 
}; 

Nhưng cách này, biên soạn không thành công trên mà khẳng định tĩnh ngay cả khi tôi không gọi get<float>(). Thậm chí nhiều hơn nữa, nếu tôi cũng loại bỏ các khẳng định tĩnh và chỉ cần trả về 0, mã biên dịch và chạy mà không bao giờ in "getter 4"!

Câu hỏi: Điều gì mang lại? Theo sự hiểu biết của tôi, các mẫu chỉ được khởi tạo nếu chúng cần thiết, nhưng Getter 4 không bao giờ được thực hiện. Tại sao trình biên dịch khởi tạo Getter 4?

Sống dụ:http://ideone.com/TCSi6G

+0

Nếu bạn viết mẫu chức năng, nếu được tạo ra, sẽ tạo ra lỗi bất kể lỗi nào, trình biên dịch có thể tạo lỗi trực tiếp. Nếu ví dụ bạn viết một StaticAssert bỏ qua đối số thứ hai của nó và vượt qua RequestedType ở đó, trình biên dịch cho phép nó vượt qua. –

+0

@Marc: Tôi không chắc tôi có được nó: bạn có thể mở rộng không? – suszterpatt

+0

Chỉ thực hiện việc này: 'mẫu void f() {static_assert (false," ");}'. Trình biên dịch từ chối nó, mặc dù tôi không bao giờ instantiating f.Vấn đề là, không thể tồn tại bất kỳ sự khởi tạo hợp lệ nào của mẫu này, và điều đó là bất hợp pháp. –

Trả lời

1

Trình biên dịch có thể biên dịch "getter 4" chức năng thành viên của bạn, bởi vì mã không phụ thuộc vào các đối số mẫu. Nếu bạn làm cho mã phụ thuộc vào các đối số mẫu, trình biên dịch không thể biên dịch nó cho đến khi bạn khởi tạo nó với một kiểu cụ thể. Một cách dễ dàng để đạt được điều này là sử dụng kiểu trong khẳng định tĩnh.

template < class RequestedType, class NonMatchingType > 
struct Getter< RequestedType, NonMatchingType, bool > 
{ 
    static RequestedType get(ContentHolder< NonMatchingType, bool >* holder) 
    { 
     cout << "getter 4" << endl; 
     StaticAssert<sizeof(NonMatchingType) == 0>::test("Type not in list"); 
     return 0; 
    } 
}; 
Các vấn đề liên quan