簡體   English   中英

C ++重載函數按返回類型

[英]C++ overload function by return type

如果我認為我對C ++一無所知,那就是你不能通過返回類型重載函數。

那么有人能解釋一下這里發生了什么嗎?

class A { public: typedef int _foo; };
class B {};

template<class T>
typename T::_foo Foo(int)
{
    cout << "Foo(int)\n"; return typename T::_foo();
}

template<class T>
typename T Foo(char)
{
    cout << "Foo(char)\n"; return typename T();
}

int main()
{
    Foo<A>(0);      // Writes "Foo(int)", as expected.
    Foo<B>(0);      // Writes "Foo(char), expected error unable to compile template.
    return 0;
}

有兩個類A和B. A定義typedef _foo,B不定義。 函數模板Foo,Foo(int)和Foo(char)有兩個重載。 Foo(int)返回T :: _ foo,Foo(char)返回T.

然后調用Foo(0)兩次。 這與Foo(int)完全匹配,所以我希望Foo <A>(0)編譯好,而Foo <B>(0)編譯失敗,因為B沒有定義模板中使用的類型_foo。

實際發生的是Foo <B>(0)完全忽略Foo(int)並實例化Foo(char)。 但是通過正常的重載決策規則,Foo(0)顯然是Foo(int)的精確匹配,並且使Foo(char)成為更可行的匹配的唯一因素是不應該考慮的返回類型。

要驗證它是影響重載決策的返回值,只需添加:

template<class T>
void Bar(int)  { typename T::_foo a; cout << "Bar(int)\n"; }

template<class T>
void Bar(char) { cout << "Bar(char)\n"; }

Bar<A>(0);      // Writes "Bar(int), as expected.
//Bar<B>(0);    // Error C2039: '_foo' : is not a member of 'B', as expected.

這清楚地表明,在沒有返回值的情況下,Foo(int)確實是正確的重載,並且如果模板無法解析從其模板參數中使用的類型,則編譯失敗是正常結果。

你沒有在返回類型上重載,你正在專門化一個函數模板,當Foo<B>(int)形成無效類型B::_foo ,從SFINAE設置的重載中刪除了特化,留下了Foo<B>(char)作為唯一可行的功能。

更詳細地說,對Foo<A>(0)的調用首先執行名稱查找以查找范圍內的所有Foo名稱,然后實例化任何函數模板以找到重載候選,然后重載決策選擇最佳匹配。

實例化函數模板的步驟產生以下兩個函數聲明:

int Foo<A>(int);
A Foo<A>(char);

過載分辨率選擇第一個作為最佳匹配。

但是,當調用Foo<B>(0) ,實例化會生成這些聲明:

<invalid type>  Foo<B>(int);
B Foo<B>(char);

第一個聲明無效,因此只有一個候選者可以進行重載解析,因此這是一個被調用的聲明。

Bar示例中,在實例化期間形成的無效類型不在函數聲明的“直接上下文”中(它位於函數定義中,即正文中),因此SFINAE不適用。

template<class T>
typename T::_foo Foo(int);

template<class T>
typename T Foo(char);

所以你的代碼聲明了這個重載的函數。 真好。

Foo<A>(0);

在這種情況下,編譯器會嘗試為上面聲明的原型填寫模板,這將是:

int Foo(int); 
A foo(char); 

因為你傳遞一個整數作為參數,第一個是更好的匹配,所以編譯器使用那個。

Foo<B>(0);

編譯器再次看到這一行,並嘗試填寫原型的模板,但......

WTFDOESNTMAKESENSE?!?!? Foo(int); 
A foo(char); 

很明顯,第一個甚至沒有意義,所以它丟棄並使用第二個重載。 這實際上與返回類型無關,它與模板原型在決定你的意思之前填寫的方式有關。 這是重新安排的示例,以澄清:

template<class T>
int foo(T::_foo) {}
template<class T>
int foo(char) {}

int main() {
    foo<A>(0); //uses the first, `int foo(int)` better than `int foo(char)`
    foo<B>(0); //uses the second, because the first doesn't work with B.

這稱為SFINAE,請注意它僅適用於模板參數,返回類型和函數參數的非常特殊情況,但不適用於函數體本身。 這就是為什么你的“驗證”導致錯誤的原因,因為它無法判斷原型中的一個函數是無效的,並且原型是在重載決定時唯一考慮的因素。

暫無
暫無

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

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