2016-07-25 83 views
3

從李普曼等人C++引物第5版,部分16.1.2:朋友比較和在C++類模板的關係運算符

//forward declarations needed for friend declarations in Blob 
template <typename> class BlobPtr; 
template <typename> class Blob; 
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&) 

template <typename T> class Blob { 
    friend class BlobPtr<T>; 
    friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 
} 

第一個問題:在線路

friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 

爲什麼在==之後出現<T>?爲什麼不簡單地寫

friend bool operator==(const Blob<T>&, const Blob<T>&); 

我添加了下面的代碼來定義運算符==和實例化類模板。它成功地編譯和鏈接:

template <typename T> 
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;} 

int main() { 
    Blob<int> a, b; 
    a == b; 
} 

如果我刪除<T>在friend聲明如下operator==,我得到一個鏈接錯誤:

Undefined symbols for architecture x86_64: "operator==(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-3ccda9.o 

顯然<T>以下operator==是必要的,但爲什麼呢?

第二個問題:如果我想定義比運營商<關係少爲同一類,我想我應該遵循的==工作模式:

1)前瞻性聲明的操作

2)申報經營者爲友,在插入附加<T>其功能我不明白

3)定義了運營商外的類。

因此,我添加以下代碼:

template <typename T> bool operator<(const Blob<T>&, const Blob<T>&); 
template <typename T> class Blob { 
    //other members as before 
    friend bool operator<<T>(const Blob<T>&, const Blob<T>&); 
} 
bool operator<(const Blob<T>&, const Blob<T>&) {return true;} 
int main() { 
    //other statements as before 
    a < b; 
} 

這將產生圍繞operator<<T>編譯錯誤,我想是因爲編譯器把<<作爲插入運算符。但是,如果我重寫朋友宣佈爲

friend bool operator<(const Blob<T>&, const Blob<T>&); 

然後我得到==類似於早期的鏈接錯誤鏈接錯誤:

"operator<(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-a85d5d.o 

我怎樣才能成功地定義操作<這個類?

(注:因爲更完全實現實現依賴於私有變量的運營商必須聲明爲朋友)

+1

請記住,像這樣聲明的朋友函數不是成員函數,它們是非成員函數,沒有''函數不完整。至於'運營商<'嘗試在運營商名稱和模板(如運營商< )之間添加一個空格。 –

+0

關於未定義的參考及其原因和解決方案(如您所知); http://stackoverflow.com/a/35891188/3747990 – Niall

+0

@JoachimPileborg - 你能提供一個對這個語法介紹的參考嗎?我甚至不知道它叫什麼,所以我很難查找它。它是模板專業化嗎?顯式實例化?對這些術語進行網絡搜索不會產生有用的結果。 – Chad

回答

0

我發佈了自己的答案,承認Joachim Pileborg和songyuanyao的方向。

我簡化了代碼,只專注於問題1。 Pileborg和Holt正確地指出,重載<只需要一個空間來幫助編譯器解析。

template <typename> class Blob; 
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&); //line 2 

template <typename T> class Blob { 
    friend bool operator==(const Blob<T>&, const Blob<T>&); //line 5 
}; 

template <typename T> 
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;} //line 9 

int main() { 
    Blob<int> a, b; //line 12 
    a == b; //line 13 
} 

此代碼在鏈接時產生錯誤。爲了理解爲什麼,我們將從標準看相關語言。

從C++ 14標準n4296,14.5.4(請參閱底部以獲取此處使用的術語摘要)。

For a friend function declaration that is not a template declaration:

(1.1) — if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,

(1.2) — if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,

(1.3) — if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template (14.8.2.6), otherwise,

(1.4) — the name shall be an unqualified-id that declares (or redeclares) a non-template function.

現在我們來看看第5行的朋友聲明,確定它是指根據上面列出的四個步驟。

(1.1)==不是模板標識;繼續...

(1.2)==不是合格ID;繼續...

(1.3)==不是合格的ID;繼續...

(1.4)因此,==是聲明(或重新聲明)非模板函數的非限定id。

根據該標準的第7.3.3節,朋友==在最內層的封閉名稱空間中聲明 - 在本例中是全局名稱空間。

當我們在第12行實例化Blob<int>時,編譯器通過用int替代類Blob中的所有出現的T來生成源代碼。在編譯器生成的代碼的朋友聲明,然後寫道:

friend bool operator==(const Blob<int>&, const Blob<int>&); 

因此,我們已經宣佈的operator==在全局命名空間(非模板)過載,與const Blob<int>&類型的參數。

當在第12行調用a == b時,編譯器開始重載解析過程。它首先查找與參數類型相匹配的任何非模板重載。當Blob<int>被實例化時,它找到operator==形式的完美匹配。鏈接器然後查找對應於最佳匹配聲明的operator==的定義,但它找不到任何內容,因爲operator==(const Blob<int>&, const Blob<int>&)從未實際定義過!

的解決方案是使用模板id(這是不是一個模板聲明)作爲友元聲明名稱:

friend bool operator== <>(const Blob<T>&, const Blob<T>&) 

friend bool operator== <T>(const Blob<T>&, const Blob<T>&) 

兩個operator== <>operator== <T>是模板-id(參見下面的術語);前者具有從函數參數列表推導出的隱式模板參數列表,後者具有明確的模板參數列表。

Blob<int>上線12實例化,編譯器生成以下代碼爲朋友的聲明:

friend bool operator== <>(const Blob<int>&, const Blob<int>&) 

friend bool operator== <int>(const Blob<int>&, const Blob<int>&) 

在任一情況下,朋友的名字是不合格template-id,所以在上面(1.1)中,朋友聲明指的是功能模板的專門化。編譯器然後找到,然後找到所需的<int>專業化的最佳模板匹配。它找到的唯一模板是在第2行中聲明的模板,並在第9行中定義,它根據需要調用它。

術語

合格-ID:具有附接的作用域運算符的標識符,例如std::string::i

unqualified-id:沒有附加範圍操作符的標識符,例如, stringi

模板id:以下摘錄(從C++ 14標準n4296,14.2)總結模板id的結構:

因此,一些模板id的將包括Foo<T>==<T>。 (==是一個operator-function-id)。請注意,與模板聲明不同,template <>未包含在模板標識表達式中。

2

why is the <T> present after == ? Clearly the <T> following operator== is necessary, but why?

因爲operator==在朋友的聲明是指函數模板,你必須明確地指定它。否則,將會聲明一個非模板函數,但是它的定義不能在以後找到。它與調用(和實例化)函數模板不一樣。

注意T可以省略,但仍需要<>。如:

// refers to a full specialization of operator== 
friend bool operator== <>(const Blob<T>&, const Blob<T>&); 

另一名候選人的辦法是定義類的聲明,這將是內聯,並可能被宣佈爲無模板函數內部操作。如:

template <typename T> class Blob { 
    ... 
    friend bool operator==(const Blob&, const Blob&) { 
     return ...; 
    } 
} 

This produces a compilation error around operator<<T>

是如你所說,應該寫爲friend bool operator< <T>(...),或friend bool operator< <>(...),或者看到我對非模板函數朋友的建議。

0

First question: in the line

friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 

why is the <T> present after == ? Why not simply write

friend bool operator==(const Blob<T>&, const Blob<T>&); 

如果刪除<T>,GCC發出警告:

warning: friend declaration ' bool operator==(const Blob< <template-parameter-1-1> >&, const Blob< <template-parameter-1-1> >&) ' declares a non-template function [-Wnon-template-friend]

你正在一個非模板函數類的朋友,所以編譯器/連接器將尋找一個非模板功能,在你的情況下:

bool operator==(const Blob<int>&, const Blob<int>&); 

......它不存在,因此鏈接器找不到它。

如果您未將<T>(或<>添加到friend聲明中),則必須爲每種類型定義一個函數,這可能不是您想要的。

Second question: If I want to define the relational less than operator < for the same class, I would guess that I should follow the pattern that worked for ==:

這是一個簡單問題的方式的C++代碼解析,您需要operator<<<之間插入一個空格。這與C++ 11之前存在的問題相同,因爲>>因爲必須使用vector<vector<int> >而不是vector<vector<int>>