2012-08-29 152 views
0

我試圖讓乘法運算符成爲名爲TVector3的模板類的朋友。我已經讀過,我可以在聲明它在類聲明中之前先聲明好友函數,但是我嘗試這麼做是徒勞的。我知道我可以簡單地定義朋友函數而不是聲明它,但我希望它能夠使用前向聲明技術。模板類中的朋友函數

特別是我試圖爲我的情況實施this解決方案。我發現this的帖子也是David Rodriguez提供的解決方案(第三版),但我不知道我做錯了什麼。

予編譯使用 '克++ template.cpp tempmain.cpp一個' 和編譯器(克++)提供了以下錯誤:

未定義參照「射線:: TVector3射線::運算符*(浮,射線:: TVector3常量&)」

爲以下代碼:

template.h:

#ifndef TVECTOR_H 
#define TVECTOR_H 

#include <cmath> 

namespace ray 
{ 
    //forward declarations 
    template <class T> class TVector3; 
    template <class T> TVector3<T> operator* (T, const TVector3<T> &); 

    template <class T> 
    class TVector3 { 

     public: 
      union { 
       struct { 
        T x, y, z; 
       }; 
       T xyz[3]; 
      }; 

     //function needed 
     friend TVector3<T> operator*<T> (T, const TVector3<T> &); 
    }; 

} 

#endif 

template.cpp:

#include "template.h" 

using namespace ray; 

//friend operator function 
template<class T> TVector3<T> operator * (T f, const TVector3<T> &v) 
{ 
    return TVector3<T>(f * v.x, f * v.y, f * v.z); 
} 

//instantiate template of type float 
template class TVector3<float>; 

tempmain.cpp:

#include <iostream> 
#include "template.h" 

int main() 
{ 
    ray::TVector3<float> v1, v2; 

    v2.x = 3; 
    v1 = 4.0f * v2; //this reports as undefined 

    std::cout << v1.x << "\n"; 

    return 0; 
} 

即完整源代碼。我究竟做錯了什麼?

回答

4

作爲一般的經驗法則,模板應該在標題中定義。如果您在cpp文件中定義它們,則需要手動實例化模板。您正在爲類模板執行此操作,但您沒有實例化operator*模板。

template TVector3<T> operator*<T> (T, const TVector3<T> &); 

此外,我會建議,而不是使用模板operator*你通過聲明和定義類模板裏面使用非模板免費功能:

template <class T> 
class TVector3 { 
//function needed 
    friend TVector3<T> operator*(T f, const TVector3<T> & v) { 
     return TVector3<T>(f * v.x, f * v.y, f * v.z); 
    } 
}; 

這有免費的優勢函數(它允許在第一個參數上進行轉換)以及模板(它將根據需要生成免費函數 - 無需手動提供所有實現)以及有限的可見性(只能通過ADL進行查找)

+0

在我的情況,我會如何實例化操作*模板?我假設我需要在cpp文件中做到這一點。我正在試圖這樣做,作爲一個練習。另外我還有一個問題,當頭文件被包含在其他cpp源文件中時,不會在頭文件中定義模板類,從而臃腫可執行文件? 感謝您的建議BTW和您的及時回覆:) – Kevin

+0

@Kevin:實例化模板的代碼是在答案,第一行。請注意,這是一個很少使用的功能,因此它可能無法編譯(如果是這樣,請告訴我),但它應該是正確的。關於代碼膨脹,模板的顯式實例更容易出現代碼膨脹。當你明確地實例化一個模板時,所有的成員函數都被實例化(以及在類模板中定義的朋友)。另一方面,通過隱式實例化(頭文件中的所有模板定義),編譯器將按需生成函數,因此可執行文件將包含更少的代碼。 –

+0

@Kevin:...雖然,因爲定義在標題中,所以多個翻譯單元*使用*(* odr-use *)相同的成員函數,編譯器將在所有翻譯單元中生成代碼,鏈接器放棄除一個定義之外的所有內容。但是在鏈接器完成後,只有每個使用函數的一個副本將在可執行文件中。同時,編譯時間可能會更長(相同的代碼將被多次生成,以後纔會被丟棄)。但這個問題很少。 –

0

你應該明確地實例化的operator *和它的工作是這樣

using namespace ray; 

//friend operator function 
template<class T> TVector3<T> ray::operator * (T f, const TVector3<T> &v) 
{ 
    return TVector3<T>(f * v.x, f * v.y, f * v.z); 
} 

//instantiate template of type float 
template class TVector3<float>; 
template TVector3<float> ray::operator*<float>(float, TVector3<float> const&); 

請記住,如果你在的地方ray::operator*編譯器的編寫operator*有沒有辦法知道你的意思ray::operator*,你是不是在全球宣佈一個新的運營商命名空間!

,不要忘記定義TVector3(T x_, T y_, T z_) :)

+0

感謝指定命名空間工作得很好:) 我還有一個問題。我將如何在全局命名空間中聲明它?我嘗試在頭文件中使用(:: operator *),但它不起作用。 – Kevin