2014-06-18 82 views
6

有以下代碼,爲什麼第一個賦值不會在Foo中調用模板operator=,但第二個呢?這裏發生了什麼?即使用戶定義的模板存在,是否有編譯器爲第一個賦值生成的?使用模板賦值運算符

#include <iostream> 

using namespace std; 

struct UberFoo { }; 

struct Foo : public UberFoo 
{ 
    template<typename T> void operator=(const T& v) { cout << "const T&" << endl; Set(v); } 
    template<typename T> void operator=(T& v) { cout << "T&" << endl; return Set(v); } 

    virtual void Set(const Foo&) { cout << "Foo::Set(const Foo&)" << endl; } 
    virtual void Set(const UberFoo&) { cout << "Foo::Set(const UberFoo&)" << endl; } 
}; 

struct Bar : public Foo 
{ 
    virtual void Set(const Foo&) { cout << "Bar::Set(const Foo&)" << endl; } 
    virtual void Set(const UberFoo&) { cout << "Bar::Set(const UberFoo&)" << endl; } 
}; 

int main() 
{ 
    Bar a, b; 

    Foo & pa = a; 
    const Foo& rb = b; 
    const UberFoo & urb = b; 

    cout << "First" << endl; 

    pa = rb; 

    cout << endl << "Second" << endl; 

    pa = urb; 

    return 0; 
} 
+0

我知道賦值運算符應該返回參考自我(做鏈接可能),但這不是重點 – relaxxx

+0

是的,一個拷貝賦值運算符始終定義,除非明確地刪除。如果你沒有定義一個,編譯器會。 –

回答

5

編譯器仍舊產生非模板operator=其中第一分配結合。在第二個作業中,模板operator=是一個更好的候選人(因爲它不涉及演員表),所以選擇一個。

你可以看到,通過添加代碼如下:

Foo& operator=(const Foo&) = delete; 

或者強迫正確的模板調用:

pa.operator=<Foo>(b); 

The standard說(重點煤礦):

12.8複製和移動課程對象,§12.8/ 17,第271頁:

甲用戶聲明的複製賦值運算符X ::運算符=是類X的非靜態非模板成員函數正好與一個參數類型X的,X &,常量X &,易失性X &或const volatile的X &

§12.8/ 18,同一頁:

如果類定義不明確聲明拷貝賦值運算符,一個聲明含蓄

+0

以及生成的簽名是怎樣的?當'T'推導爲'Foo'簽名是相同的:'void運算符=(const的富&)',所以這是不如編譯器生成的一個。爲什麼'T'不能被推斷爲'Foo'? – relaxxx

+0

@relaxxx:它可能與複製賦值運算符「一樣好」,但根據此答案中引用的規則,它不是複製賦值運算符。 'T'可以推斷爲'Foo'但沒有必要的發生,因爲已經有一個匹配的非模板函數調用,並且優先。 –

+0

@relaxxx正如LRIO所說的,簽名是一樣的,但功能是模板化的。在重載解析期間,非模板比模板匹配得更好,所以刪除的操作符被選爲最佳候選。 –

1

爲什麼第一次分配不調用模板運營商= Foo中,但第二個呢?這裏發生了什麼?

除了William對隱式生成的複製賦值運算符函數的解釋所解釋的事實之外,重載解析在這方面也有所發揮。以下是第一個賦值運算符的考生,模板參數替換後,由編譯器所看到:

Foo& operator=(const Foo&);  // implicitly generated 
void operator=(const Foo&);  // template generated 

所有的事情都是平等的,非模板函數仍然是首選函數模板。按C++ 11(N3337草案),13.3.3/1(重點煤礦)

根據這些定義,一個可行的函數F1被定義爲比另一個可行函數F2如果一個更好的功能所有參數i,ICSI(F1)不是比ICSI(F2)更差的轉換序列,然後對於某些參數j,ICSj(F1)是比ICSj(F2)更好的轉換序列,或者if不是,

- 上下文是通過用戶定義的轉換進行初始化(請參閱8.5,13.3.1。5,和13.3.1.6)和F1的到目的地類型的返回類型(即, 實體的類型被初始化)比從 返回類型的標準轉換序列更好的轉換序列 標準轉換序列F2的目標類型。 [...],或者,如果不說,

- F1是一個非模板函數和F2是一個函數模板特,或者,如果不說,

- F1和F2函數模板特,而對於F1函數模板比​​模板用於根據在14.5.6.2描述的部分排序規則F2更專門 。

這就解釋了爲什麼非模板超載採摘。您可以驗證同樣用小練習:

void print(int, int) { std::cout << "int"; } 
template <typename T> void print(T, T) { std::cout << "T"; } 
print(1, 2); // prints int 
print<>(1, 2); // prints T 

至於第二次分配,它看到

Foo& operator=(const Foo&);  // implicitly generated 
void operator=(const UberFoo&); // template generated 

這裏生成的模板函數比隱式生成之一,因此它是一個更匹配選擇。