2011-03-30 90 views
4

我有一個包含私有的typedef,和幾個成員 函數的類:使用模板暴露私有的typedef,

class Foo 
{ 
private: 
    typedef std::blahblah FooPart; 
    FooPart m_fooPart; 
    ... 
public: 
    int someFn1(); 
    int someFn2(); 
}; 

幾個成員函數需要以類似的方式使用m_fooPart,所以我 要把它放在一個函數中。我儘可能在匿名的 名稱空間中放置助手函數,但在這種情況下,他們需要知道FooPart是什麼。在 土地的良好定義的行爲仍然

namespace 
{ 
    template <typename T> 
    int helperFn(const T& foopart, int index) 
    { 
    ... 
    return foopart.fn(index); 
    } 
} 

int Foo::someFn1() 
{ 
    ... 
    return helperFn(m_fooPart, ix);  
} 

通過迫使編譯器生成的FooPart類型,我在:所以,我已經做到了這一點?有沒有更優雅的方式 完成這個不增加Foo的大小或公開 什麼是私人的?

+3

爲什麼不把輔助函數的Foo'的私有成員'?你認爲採取這種方法會失去什麼? – ildjarn 2011-03-30 20:28:48

+0

準確地說,模板將* not *暴露'typedef',而是讓編譯器在調用的地方解析類型。 – 2011-03-31 07:41:01

+0

@dribeas,謝謝。這似乎有點奇怪,類型可以隱式地(由編譯器)解決,但不是明確地(由我)解決。 – criddell 2011-04-01 13:02:15

回答

4

是的,該方法可以產生明確定義的符合標準的行爲。

也就是說,向類中添加成員函數並不會增加類的大小(假設您的意思是sizeof運算符的結果),所以我不確定您在創建助手函數時感覺到什麼缺點私人成員Foo

+0

我猜這裏的_size_的意思是「.h文件中的字符數」 – anatolyg 2011-03-30 20:42:27

+2

@anatolyg:也許,但我不希望 - 這是一個非常愚蠢的指標。 – ildjarn 2011-03-30 20:43:46

+0

我說「大小」,但我真的應該說「複雜性」。主要是保持類聲明儘可能簡單。 – criddell 2011-03-31 03:09:09

3

簡單的回答:使typedef公開。

這會泄漏實現的一個小細節(實際的內部類型),但是因爲它是typedefed,你可以在任何時候重新定義它,它應該沒問題。

簡單一點:交友助手功能,提供訪問您的內部類型。

第二種方法的問題在於,您不僅授予對typedef的訪問權限,還授予對您班級的所有私有部分的訪問權限,這可能不是最好的主意。無論如何,因爲這是一個內部幫助函數,所以它是在你自己的控制之下的,它應該沒問題。 (現在我想起來了,你可能想聲明函數在一個命名的命名空間中,以使friend聲明成功)

更簡單:在實現文件中創建一個單獨的typedef,並確保它們是同步的。

您可以確保類型與一小段元編程相同,same_type<T,U>模板將提供一個true值(如果兩個類型相同,否則爲false)。如果typedef只在一個地方更改,則靜態聲明將觸發錯誤

返回簡單:提供typedef或直接使用類型,而不使用靜態聲明。

你正在調用一個函數(這不應該是你的代碼中的模板),並傳遞一個引用。如果typedef在類中發生更改,則調用將失敗,編譯器會告訴您。

我會去的最後一個選項,雖然它可能看起來有點粗糙,少細膩比別人,但事實是,這是唯一沒有被其他人一個實現細節,你完全控制之下的代碼和好,簡單就更好。

編輯,之後的評論。

我開始寫這個作爲評論,但它變得太長了,所以我將它添加到答案。

這個解決方案本身並沒有什麼錯,除了不必要的做一個通用函數,以後一些錯誤信息可能不會像使用非通用簽名那樣簡單。請注意,template而不是公開typedef(問題標題建議),而是它會使編譯器推斷在調用的地方的類型。

如果更改typedef,而不是得到一個錯誤,指出該參數helperFn不能對現有的功能相匹配,則類型推斷,功能匹配,但你會在helperFn得到一個錯誤更深,如果你使用不再存在的類型的屬性。或者更糟糕的是,如果這種類型的語義發生了變化,那麼您甚至可能不會收到錯誤。

認爲typedef的是std::list<X>的,而且在你迭代它與這個簡單的正確的for循環的功能:

for (typename T::iterator it=x.begin(), end=x.end(); it != end;) { 
    if (condition(*it)) 
     it = x.erase(it); 
    else 
     ++it; 
} 

你能趕上改變的typedef std::vector<X>就會有效果?即使代碼現在不正確,編譯器也不能。是否這樣寫for for循環是一個好主意,或者爲什麼它不僅僅使用擦除 - 刪除成語是不同的問題(事實上,前一個循環是可以說是更好的擦除刪除一個列表),具體情況是語義已經改變,並且因爲類型在語法上與前一個兼容,所以兼容編譯器不會注意到代碼是錯誤的,它不會指向你的那個函數,並且機會是你不會審查/重寫它。

+0

首先,作爲模板的函數有什麼問題?其次,關於如果我的typedef更改,通知編譯器的好處。我開始認爲你的最後一個選擇是順其自然的方式(@anatolyg給了我同樣的建議)。 – criddell 2011-03-31 03:14:43

+0

@criddell:我已經更新了答案,並對您的方法的問題進行了一些具體評論。 – 2011-03-31 08:02:01

1

我想這是通用編程的想法 - 做不知道它的類型的部分Foo的東西。 一個更「傳統」(強類型的,無聊的,可讀的代碼複製 - 你的名字)的方法是明確提及類型:

int helperFn(const std::blahblah& foopart, int index) 
{ 
    ... 
    return foopart.fn(index); 
} 
+0

實際類型非常多,時間更長。我使用了一個typedef來避免代碼重複(如你所提到的),併爲該類型提供一個描述性名稱。 – criddell 2011-03-31 03:11:44