2012-02-13 59 views
2

說我有一束構件的結構:如何使用模板表示成員之間的約束?

struct foo { 
    int len; 
    bar *stuff; 
}; 

作爲恰巧stuff將指向bar s表示是len長的陣列。我想編碼在stuff的類型。因此,像:

struct foo { 
    int len; 
    DependentLength<bar, &foo::len> stuff; 
}; 

然後我可以實現DependentLength表現得像一個指向柵陣列,但是,試圖尋找比foo::len更大的索引時斷言。然而,我不能實現DependentLength<&foo::len>::operator[],因爲operator []只接受一個參數,即索引,並且它需要知道'foo'對象的位置以取消引用成員指針模板參數並執行斷言檢查。

但是,我碰巧知道DependentLength只會在這裏用作'foo'的成員。我真正想要做的是告訴DependentLength在哪裏找到相對於它自己的len ,而不是相對於foo指針。所以像DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;,但這不是合法的C++。邪惡的語言可以使這項工作成功嗎?

+0

爲什麼不首先使用像'std :: vector'這樣的容器? – 2012-02-13 19:02:28

+2

反轉關係:struct foo {std :: vector bar; int len()const {return bar.size(); }};' – 2012-02-13 19:07:59

+0

我實際上是在一個打包結構的上下文中查看這個線索的,因此我不能只重新排列布局。 – 2012-02-13 21:13:37

回答

3

因此,像DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;

你問模板基於動態特性在運行時傳遞給他們......不會對模板工作進行計算,因爲它們必須被實例化其值允許編譯創建compile time的模板參數所要求的代碼。因此傳遞給模板的任何值都必須在編譯時解析,而不是運行時解析。您將不得不使用動態容器類型。例如,std::vector符合您的要求,如果您超出了底層容器的範圍,std::vector::at()函數將引發異常。不幸的是,這並不像static_assert那樣方便,但是再次使用static_assert對於這種情況是不可能的,因爲您需要運行時檢查邊界。此外,std::vector還包含operator[]的重載,迭代器,對其大小的查詢等。

+0

我沒有要求模板對運行時值進行操作。 &foo :: len和&foo :: stuff都是編譯時常量。他們是偏移量,而不是絕對地址。 – 2012-02-13 21:07:40

2

您可以告訴模板要用作長度的成員的偏移量。

template<typename T, typename LEN_T, ptrdiff_t LEN_OFFSET> 
class DependentArray 
{ 
public: 
    T& operator[](LEN_T i_offset) 
    { 
    if (i_offset < 0) throw xxx; 
    if (i_offset > this->size()) throw xxx; 
    return this->m_pArr[i_offset]; 
    } // [] 
private: 
    LEN_T& size() 
    { 
    return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + LEN_OFFSET); 
    } //() 
private: 
    T* m_pArr; 
}; 

struct foo 
{ 
    int len; 
    DependentArray<bar, int, -sizeof(int)> stuff; 
}; 

編輯2:

思想另一溶液。使用一類有利於FOO只供應大小字段的偏移,並定義其方法foo被定義並且可以計算偏移後:

#define MEMBER_OFFSET(T,M) \ 
    (reinterpret_cast<char*>(&reinterpret_cast<T*>(0x10)->M) - \ 
    reinterpret_cast<char*>(reinterpret_cast<T*>(0x10))) 

template<typename T, typename LEN_T, typename SIZE_OFFSET_SUPPLIER> 
class FooDependentArray 
{ 
public: 
    T& operator[](LEN_T i_offset) 
    { 
    if (i_offset < 0) throw xxx; 
    if (i_offset > this->size()) throw xxx; 
    return this->m_pArr[i_offset]; 
    } // [] 
private: 
    LEN_T& size() 
    { 
    const ptrdiff_t len_offest = SIZE_OFFSET_SUPPLIER::getOffset(); 

    return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + len_offset); 
    } //() 
private: 
    T* m_pArr; 
}; 

struct FooSizeOffsetSupplier 
{ 
    static ptrdiff_t getOffset(); 
}; 

struct foo 
{ 
    int len; 
    DependentArray<bar, int, FooSizeOffsetSupplier> stuff; 
}; 

ptrdiff_t FooSizeOffsetSupplier::getOffset() 
{ 
    return MEMBER_OFFSET(Foo,m_len) - MEMBER_OFFSET(Foo,m_pArr); 
} //() 

這使得能夠從富添加和刪除構件。

+0

在您的size()成員函數中,如何將任意值轉換爲指針類型然後對其進行解引用是安全的?此外,將參考返回給臨時對象也是不安全的。我相信你想把這個指針轉換回'LEN_T'而不是'LEN_T *',然後返回一個'LEN_T'而不是'LEN_T&'。 – Jason 2012-02-13 21:09:44

+0

這就是我迄今爲止所做的,但是希望有一種方法不需要我每次向結構添加新成員時手動更改模板參數。 – 2012-02-13 21:09:44

+0

@Jason:這不是任意的。他將'this'轉換爲char *,以便指針運算以字節爲單位,然後備份4個字節以查找foo結構中的DependentArray之前的int。 – 2012-02-13 21:11:47