2010-08-08 15 views
2

我的容器需要存儲一些關於其元素的信息。通常,我將其與元素分開存儲。不過,我想通過專門爲外部使用元素結構類型的字段來爲用戶提供保存內存的可能性。例如:爲「外部使用」專用任意類的字段

struct MyStuff 
{ 
    int   foo; 
    char   bar; 
    mutable char dedicated_for_external_use; // Because of alignment, this field 
              // won't increase sizeof (MyStuff) 
}; 

這裏的想法是該字段不能被除元素的容器之外的任何東西訪問。由於容器存儲副本(非常類似於std::vector),因此如果將任何給定值x添加到多個容器中,這不會成爲問題。

你會如何設計一個接口,如果可能的話,會符合以下要求?

  • 應該是完全可選的。即應該可以自動確定給定類型是否提供這樣的字段,否則容器只會在可用時使用它。
  • 理想情況下,不會依賴於類型特徵等,因爲我需要最大的編譯器兼容性。
  • 應該很容易使用。即如果可以並且想要爲類型MyStuff啓用此優化,則可以使用3行代碼執行此操作,而不是25次。另一方面,內部複雜性並不重要。
  • 應該最好完全排除誤報。我的意思是:如果你檢查字段foo_bar有一個小的可能性,這個字段存在一個完全不相關的原因(我認爲duck-typing只是不適用於C++)。更好的方法是檢查類型是否從我的庫中繼承標記類ProvidesExternalUseField,因爲這不會是偶然的。

編輯

我知道Boost.Intrusive,但我要的是不同的東西。如果我以這種方式創建一個帶有單個char字段的鉤子類,它在很多情況下不能用於節省內存。如果繼承類型具有int作爲第一個字段,則char字段將填充爲4個字節。即你經常需要類型內部的複雜的知識,能夠在「擠」這樣的extern使用的領域,但產業並沒有真正提供它:

struct hooks { mutable char dedicated_for_external_use; }; 
struct MyStuff : hooks 
{ 
    int   foo; 
    char   bar; 
}; 

這裏,MyStuff大小爲12個字節,不是8.

+0

也許Boost.Intrusive(http://www.boost.org/doc/libs/1_43_0/doc/html/intrusive.html)和'boost :: intrusive_ptr'(http://www.boost.org/ doc/libs/1_43_0/libs/smart_ptr/intrusive_ptr.html)可能會提供一些啓示。 – Philipp 2010-08-08 13:17:15

+1

當你說「最大編譯器兼容性」時,這在實踐中意味着什麼?你需要它的編譯器有多糟糕? – jalf 2010-08-08 13:24:05

+0

@jalf:嗯,最好它應該在C++ 98兼容的編譯器上工作。我的意思是,如果可能的話,我寧願不使用任何非標準擴展或C++ 0x。 – doublep 2010-08-08 15:12:07

回答

0

對於數據結構從標記接口派生的情況,可以使用部分模板特化。

比方說,你的標記接口的類看起來是這樣的:

class ProvidesExternalUseField 
{ 
public: 
    char GetExtraField() { return 0; } 
    void SetExtraField (char newVal) {} 
}; 

它不是虛擬的宗旨:我們不希望一個虛函數表指針添加到數據類只是這一點。

現在讓我們實現一個簡單的容器類:

template <class T> 
class Container 
{ 
public: 
    char GetExtraValue() 
    { 
     return 0; // here we cannot know if T is derived from the marker 
    } 
private: 
    T m_t; 
}; 

這裏是我們如何將其更改爲2例之間的區別:

template <class T, bool DoesTProvideExternalUseField> 
class ContainerImpl 
{ 
public: 
    char GetExtraValue() { return 0; } 

private: 
    T m_t; 
}; 

template <class T> 
class ContainerImpl<T, true> 
{ 
public: 
    char GetExtraValue() { return m_t.GetExtraField(); } 
private: 
    T m_t; 
}; 

template <class T> 
class Container: public ContainerImpl<T, 
             boost::is_base_of<ProvidesExternalUseField,T>::value> 
{ 
}; 

現在你可以這樣定義的結構:

struct A 
{ 
    int m_intVal; 
}; 

struct B: public ProvidesExternalUseField 
{ 
    char GetExtraField() { return m_extraField; } 
    void SetExtraField (char newVal) { m_extraField = newVal; } 

    int m_intVal; 
    char m_charVal; 
    char m_extraField; 
}; 

並以完全相同的方式使用容器類:

Container<A> a; 
Container<B> b; 

您還可以通過使用poiter-to-member作爲模板參數來進一步自動化(模板化)標記接口中的getter和setter。