2012-02-22 68 views
1
#include <iostream> 

template< typename U > 
struct base { 
    template< typename T > 
    base const & operator<<(T x) const { 
     std::cout << sizeof(x) << std::flush; 
     return *this; 
    } 
}; 

template< typename U > 
struct derived : public base<U> { 
    using base<U>::operator<<; 

    derived const & operator<<(float const & x) const { 
     std::cout << "derived" << std::flush; 
     return *this; 
    } 
}; 

int main() { 
    unsigned char c(3); 
    derived<double> d; 
    d << c; 
    d.operator<<(c); 
    return 0; 
} 

您能解釋一下爲獲得上述代碼的正確答案而涉及的規則(與模板相關的重載和覆蓋,積分提升......)嗎?它有效嗎?如果規則太長,請提供文獻指針。最新的編譯器對正確的結果持不同意見。 gcc-4.6和icpc-12.1.0聲稱「11」是正確答案,但VS2010由於含糊不清而拒絕編譯d << c;,但接受d.operator<<(c);。後者輸出1 iirc。那麼誰是對的,誰錯了?以下代碼的ISO C++標準符合性結果

+0

嘿,有什麼區別?如果'd << c'不能在MSVC上編譯,那麼只需打印一個'1'。其他編譯器打印兩個'1'。或者我錯過了什麼? – 2012-02-22 14:19:49

+0

你需要認識到,沒有涉及的數據類型會有sizeof == 1:8也許,4肯定,1爲什麼不呢。但是11看起來真的很像兩個'1'。 – 2012-02-22 14:22:57

+0

你想要什麼?試試編譯器? – 2012-02-22 14:23:42

回答

3

「11」是正確的輸出。

對於這兩個表達式,派生運算符< <和基本運算符< <都是候選項。然後根據它們所需的隱式轉換序列對候選進行比較。因爲基本運算符< <是一個模板函數,其中類型T已被推導爲與參數匹配,因此它在兩種情況下都表現爲更好的匹配。

確切的規則很長。您可以在13.3條目中找到詳細信息。當前C++草案標準的過載分辨率,與工作組論文的this list相關的n3337。

如果你問MSVC爲什麼不編譯一條語句,我不太確定,但是當多個計算出來的ICS不相互比較時,函數調用是不明確的(如13.3 0.3)。 MSVC似乎計算錯誤的ICS中的d << c的情況下,過載的至少一個,但診斷沒有給出任何更多的細節:

error C2666: 'derived<U>::operator <<' : 2 overloads have similar conversions 
     with 
     [ 
      U=double 
     ] 
     ConsoleApplication1.cpp(24): could be 'const derived<U> &derived<U>::operator <<(const float &) const' 
     with 
     [ 
      U=double 
     ] 
     ConsoleApplication1.cpp(14): or  'const base<U> &base<U>::operator <<<unsigned char>(T) const' 
     with 
     [ 
      U=double, 
      T=unsigned char 
     ] 
     while trying to match the argument list '(derived<U>, unsigned char)' 
     with 
     [ 
      U=double 
     ] 
+0

我相信你是對的。我認爲微妙之處在於C++想要在模板函數上選擇非模板函數,但也希望選擇更好的轉換序列。在這種情況下,模板的轉換順序更好。 13.3.3.1顯示了何時非模板函數被認爲比模板函數更好,並且僅當非模板函數沒有更差的轉換序列時才適用。但是,在這種情況下,非模板函數的轉換順序確實較差。 – 2012-02-22 15:26:17

0

它不會編譯,因爲你問到operator <<被自動調用。它就像有operator +,以及有轉換運算符,可以轉換爲基本類型(比如說,到int)。例如:

class Conversion 
{ 
public: 
    operator int() 
    { 
     return 5; 
    } 

    int operator +(int x) 
    { 
     return 10; 
    } 
}; 

,並用它喜歡:

Conversion conv; 
conv + 1.0; 

這裏operator +不能隱式調用,因爲operator int也是可能的。如果你通過,int,但是,operator +(int)將被調用。要調用運營商+與double,我們可以這樣做:

conv.operator+(1.0); 

我不知道編譯器的規則,並嚴格標準定義。

我還發現,如果我們改變basederived類非模板類,這個問題將依然存在(在VC10/11):

struct base { 
    base const & operator<<(int x) const { 
     std::cout << sizeof(x) << std::flush; 
     return *this; 
    } 
}; 

struct derived : public base{ 
    using base::operator<<; 
    derived const & operator<<(float const & x) const { 
     std::cout << "derived" << std::flush; 
     return *this; 
    } 
}; 

int main() 
{ 
    derived d; 
    d<<10.0; // ERROR 
}