2014-01-30 69 views
0

我試圖提高對模板的理解。作爲一個測試,我想過載流operator<<,以便它將與STL中的(模板化的)basic_ostream一起在模板類上運行。下面是一個小例子:正確地聲明模板運算符<<(basic_ostream <charT,traits>,myClass < T >)爲朋友

#include <iostream> 

using namespace std; 

//Forward declarations 
template< typename T > 
class Container; 
template< typename T, typename charT, typename traits > 
basic_ostream< charT, traits >& 
    operator<<(basic_ostream< charT, traits >& out, 
           const Container<T>& a_container); 

//Definitions 
template< typename T > 
class Container 
{ 
public: 
    Container(T a_value): value(a_value){} 

private: 
    T value; 

    template< typename charT, typename traits > 
    friend basic_ostream< charT, traits >& 
     operator<<(basic_ostream< charT, traits >& out, 
        const Container<T>& a_container); 
}; 

template< typename T, typename charT, typename traits > 
basic_ostream< charT, traits >& 
    operator<<(basic_ostream< charT, traits >& out, 
       const Container<T>& a_container) 
{ 
    out << a_container.value; 
    return out; 
} 

//Main 
int main(void) 
{ 
    Container<int> my_container(42); 

    cout << my_container; 

    return 0; 
} 

然而,當我嘗試編譯此代碼似乎鏈接器無法找到重載函數:

/home/Ae6PGM/cc5xj2iM.o: In function `main': 
prog.cpp:(.text.startup+0x21): undefined reference to `std::basic_ostream<char, 
std::char_traits<char> >& operator<< <char, std::char_traits<char> > 
(std::basic_ostream<char,  std::char_traits<char> >&, Container<int> const&)' 
collect2: error: ld returned 1 exit status 

我可以修復這個錯誤,如果我做每一個模板實例我的超載operator<<的朋友,但如果只有與容器類型匹配的實例是朋友,那麼它似乎更正確。這是一個鏈接到工作,但不正確的verion:http://ideone.com/DNQzlB

另一種選擇是將函數定義放在類聲明中,但是如果可能的話,我想避免這種情況。

有一個類似的,但稍微簡單的問題here。有一個很好的答案,但我無法弄清楚如何適應我的問題。

感謝,

安德魯

+0

我開始認爲這是不可能的,因爲它要依賴於未在C++支持的部分專用函數模板。那是對的嗎? –

+0

我想這裏的答案可能會有所幫助:http://stackoverflow.com/questions/1458752/template-friendship –

+0

'boost'庫定義了類中的這種類型的朋友函數。請參閱:http://www.boost.org/doc/libs/1_55_0/boost/random/poisson_distribution.hpp –

回答

0

模板的原始申報了三個模板參數{ T, charT, traits }friend聲明只用了2 { charT, traits }。你會得到一個未定義的參考,因爲編譯器將這些視爲兩個單獨的函數,其中一個函數尚未定義。

你需要給它一個第三個模板參數:

template< typename T2, typename charT, typename traits > 
    friend basic_ostream< charT, traits >& 
     operator<< (basic_ostream< charT, traits >& out, 
        const Container<T2>& a_container); 
+0

這將允許代碼編譯和鏈接,但它會使該函數的每個實例成爲朋友。即使函數的類型'T2'與容器的類型'T'不匹配的情況也是如此。這是一個合理的解決方法,但我想了解是否有更正確的方法來做到這一點。 –

+0

@ andrew.punnett這*是正確的方式來做到這一點。如果你想限制它到一個特定的模板參數,它會變得更加混亂。 – 0x499602D2

+0

@ andrew.punnett關於將插入器限制到某個模板參數,你究竟想達到什麼目的? – 0x499602D2

0

謝謝你問這個問題。我通過審查瞭解了很多。

這就是說,我想我明白你想問什麼。如果我正確地理解了你,你試圖實現和控制你的function template的隱式實例化。

雖然您在此處發佈的示例是operator<<重載,但使用模板進行此泛型編程的機制與用於函數模板實現的機制相同。

當你聲明cout << my_container時,你實例化的是超負載的operator<<。但是如果你想實例化一個非模板流對象,你將不得不修改你想要讀取的內容。

由於類數據成員值是私有的,因此可以使用指針明確指向該數據成員並使用該指針來實例化非模板流對象。看看下面的例子:
int* private_value = (int*)&my_container;
*private_value應該允許你訪問私有成員,儘管封裝。

如果你想提高數據成員的安全性,那麼爲了防止這樣的實現,需要更深的封裝。

可以通過可能包括get()函數和訪問value來實現模板參數推導(如果這正是您正在尋找的),從而實例化非模板流對象。

相關問題