簡體   English   中英

模板化運算符重載解決方案,成員與非成員函數

[英]Templated operator overload resolution, member vs non-member function

嘗試使用clang-3.4(從git編譯)時,它無法編譯我的一個抱怨歧義的項目,同時解決了重載的運算符。 我發現有兩個模板化的運算符,其中一個被聲明為成員函數,另一個被聲明為非成員函數,兩者看起來都很好地匹配。

以下SSCCE演示了這種情況:

#include <iostream>

struct ostr {
        std::ostream& s;

        template<class T>
        ostr& operator<<(const T& x) { s << x; return *this; }
};

struct xy {
        double x, y;
};

template<class Stream>
Stream& operator<<(Stream& s, const xy& x) {
        s << "[" << x.x << ", " << x.y << "]";
        return s;
}

int main() {
        ostr os{std::cout};
        xy x{4, 5};
        os << "Value is: " << x <<"\n";
}

該項目編譯罰款之前,我再次檢查這個SSCCE幾個編譯器( gcc 4.54.64.74.8clang 3.3 ),他們都編譯沒有任何警告(與-Wall -Wextra -pedantic )。 所有編譯器均設置為C ++ 11 / C ++ 0x標准。 將ctor添加到ostr ,即使在MSVC 20122010上,它也可以很好地編譯)

使兩個operator<<都成為非成員在所有編譯器中都表現出歧義(如預期)

瀏覽了標准草案( N3242N3690 )后,我沒有找到任何使成員函數/運算符比非成員函數更匹配的東西。

因此,我未能證明clang-3.4是錯誤的,我想知道誰是正確的。 因此,我的問題是:

  • 此代碼有效嗎? 成員運算符/函數是否應該比非成員運算符/函數更好地匹配,並且這是clang-3.4中的錯誤?
  • 還是所有其他編譯器都是錯誤的/太寬容了?

我知道將第二個operator<<更改為非模板函數(使用std::ostream而不是template參數)將解決歧義並按預期工作,但這不是重點。

重載解析只是為了重載解析的目的而向成員函數添加了一個附加參數:

[over.match.funcs] / 2

候選函數集可以包含要針對同一參數列表解析的成員函數和非成員函數。 為了使參數列表和參數列表在此異構集合中具有可比性,因此成員函數被視為具有一個額外的參數,稱為隱式對象參數 ,該參數表示已為其調用成員函數的對象。

/ 4

對於非靜態成員函數,隱式對象參數的類型為

—“不帶ref限定符或帶有& ref限定符聲明的函數的“對cv X左值引用”

- “右值參考CV X ”為功能與聲明&& REF-限定符

其中X是該函數是成員的類,而cv是成員函數聲明上的cv -qualification。

遵循一些特殊規則,例如,允許將右值綁定到此隱式對象參數(用於在右值上調用不帶ref限定符的成員函數,例如ostr{std::cout}<<"hello" )。


該函數簽名,包括我們需要比較的重載隱含對象的參數是:

template<class T>
ostr& ostr::operator<<(ostr&, const T&);    // F1

template<class Stream>
Stream& ::operator<<(Stream&, const xy&);    // F2

替換os << x ,我們得到相同的簽名:

ostr& ostr::operator<<(ostr&, const xy&);
ostr& ::    operator<<(ostr&, const xy&);

因此,[over.match.best] / 1中只有一個“平局者”可以解決歧義。 實際上,可以采用一種方法,即“ F1F2更專業”(反之亦然):功能模板的部分排序。

注意在部分排序[temp.func.order] / 3的描述中再次指定添加隱式對象參數的過程。


對於F1F2部分排序(如上定義),我們首先創建兩個唯一的類型:

struct unique_T {};
struct unique_Stream {};

然后,通過將模板參數T替換為唯一類型unique_T (對於F2同樣),將F1轉換為F1'

ostr& ostr::operator<<(ostr&, const unique_T&);
ostr& ::    operator<<(unique_Stream&, const xy&);

現在,使用轉換后的函數F1'的參數來推導未轉換的F2的模板參數:

ostr a0;
unique_T a1; // no reference, no cv-qualifier
::operator<<(a0, a1) // does template argument deduction succeed?

// reminder: signature of ::operator<<
template<class Stream>
Stream& ::operator<<(Stream&, const xy&);

推導成功了a0 [ Stream = ostr ],因此F1 ostr&類型被認為至少與F2的相應第一個參數的類型一樣專門化( Stream&Stream是模板參數)。 我不確定第二個參數a1會發生什么,因為::operator<<的第二個參數沒有推論(它的類型為const xy& )。

現在,我們使用F2'參數重復該過程,並嘗試推導F1的模板參數:

unique_Stream a0;
xy a1;
ostr::operator<<(a0, a1);

// reminder: signature of ostr::operator<<
template<class T>
ostr& ostr::operator<<(ostr&, const T&);

在此,第一個自變量沒有演繹,但第二個自變量[ T = xy ]則成功推演。

我得出結論,沒有功能模板更專業。 因此,由於歧義,過載解析將失敗

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM