2012-08-29 70 views
12

在重載解析期間,函數對象是否與常規函數處理方式不同?如果是這樣,怎麼樣?函數對象如何影響重載分辨率?

我所遇到以下情況下具有等效可調用的功能對象替換的函數改變了代碼的含義是:

#include <iostream> 

namespace N 
{ 
    enum E { A, B }; 

    void bar(E mode) { std::cout << "N::bar\n"; } 
} 

template <typename... Args> 
void bar(Args&&... args) { std::cout << "global bar\n"; } 

int main() 
{ 
    bar(N::A); 
} 

這裏的輸出是「N ::欄」。到目前爲止,這麼好:N :: bar被ADL發現,N :: bar和全局條都是精確匹配,並且N :: bar是首選,因爲它不是模板。

但是,如果我改變了全球的酒吧是一個函數對象,就像這樣:

#include <iostream> 

namespace N 
{ 
    enum E { A, B }; 

    void bar(E mode) { std::cout << "N::bar\n"; } 
} 

struct S 
{ 
    template <typename... Args> 
    void operator()(Args&&... args) { std::cout << "global bar\n"; } 
}; 
S bar; 

int main() 
{ 
    bar(N::A); 
} 

現在的輸出是「全球性吧」。爲什麼區別?

回答

12

這裏的重要一點是,如果查找確定名稱是函數調用中的函數,則ADL只會執行。在第二種情況下,發現bar對象而不是函數,所以表達式bar(N::A)不是函數調用,而是operator()對象bar的應用。因爲它不是函數調用,所以ADL不會引入,並且不考慮N::bar

3.4.1/3

爲用作一個函數調用在3.4.2中描述的後綴表達式不合格的名稱查找。 [注意:爲了確定(在解析期間)表達式是否爲函數調用的後綴表達式,通常的名稱查找規則適用。 3.4.2 [ADL]中的規則對錶達式的語法解釋沒有影響。

另一種方式看它是要注意到,ADL將增加新的功能到組重載功能,但在第二個例子中不存在這樣的組:查找發現的對象和的一個構件對象被調用。

+2

今天我學到了,謝謝大衛。 – ildjarn

+0

我不認爲3.4的音符。1/3是相關的,因爲'bar' *是函數調用表達式中的後綴表達式(沒有函數作爲操作數,但最終調用一個!)。注意3.4.2沒有改變「x(y)」的語法含義 - 如果它是一個函數調用,它會保留一個函數調用,而不考慮3.4.2決定做什麼。 –

+0

@ JohannesSchaub-litb:定期查找會發現'bar'是一個對象,並且作爲一個對象'bar(x)'代表'operator()'應用於該實例。雖然運算符的應用程序是函數調用,但它是*成員函數*調用,因此ADL不適用。 –

4

參見3.4.2p3,它說

設X是由不合格查找(3.4.1)中產生的查找集和令Y是由參數依賴查找產生的查找集(定義如下)。如果X包含

  • ...
  • 聲明既不是一個函數或函數模板

則Y是空的。

如果不存在這樣的規則,那麼您是對的:ADL會將您的其他函數添加到重載集。實際上,13.3.1.1p1依賴於它:它有兩個分支;一個用於函數調用表達式,其中操作數表示一個類對象,另一個操作數表示一個或多個函數或函數模板。

+0

+1,在閱讀標準的這部分內容後,你提供的引用是我引用的註釋的原因。 –

+0

@david你誤解了它 –