簡體   English   中英

SFINAE和重載函數的地址

[英]SFINAE and the address of an overloaded function

我正在嘗試在另一個函數的參數( foo1 / foo2 )的上下文中解析重載函數( bar )的地址。

struct Baz {};

int bar() { return 0; }
float bar(int) { return 0.0f; }
void bar(Baz *) {}

void foo1(void (&)(Baz *)) {}

template <class T, class D>
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}

int main() {
    foo1(bar);      // Works
    foo2<Baz>(bar); // Fails
}

foo1沒有問題,它明確指定了bar的類型。

但是,除了一個版本的barfoo2通過SFINAE禁用自身,無法使用以下消息進行編譯:

main.cpp:19:5: fatal error: no matching function for call to 'foo2'
    foo2<Baz>(bar); // Fails
    ^~~~~~~~~
main.cpp:15:6: note: candidate template ignored: couldn't infer template argument 'D'
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
     ^
1 error generated.

據我所知,C ++無法同時解析重載函數的地址並執行模板參數推導。

這是原因嗎? 有沒有辦法讓foo2<Baz>(bar); (或類似的東西)編譯?

正如評論中所提到的, [14.8.2.1/6] (工作草案, 從函數調用中推導出模板參數 )在這種情況下的規則(強調我的):

當P是函數類型, 函數指針類型或指向成員函數類型的指針時:

  • 如果參數是包含一個或多個函數模板的重載集,則該參數將被視為非推導的上下文。

  • 如果參數是重載集(不包含函數模板) ,則嘗試使用集合中的每個成員進行試驗參數推導。 如果僅對其中一個重載集成員進行推導成功,則該成員將用作推導的參數值。 如果對重載集的多個成員進行推導成功,則將該參數視為非推導上下文。

一旦扣除結束,SFINAE就會參與游戲,因此無法解決標准規則。
有關更多詳細信息,您可以在上面鏈接的項目符號末尾看到示例。

關於你的上一個問題:

有沒有辦法讓foo2<Baz>(bar); (或類似的東西)編譯?

兩種可能的選擇:

  • 如果您不想修改foo2的定義,可以將其調用為:

     foo2<Baz>(static_cast<void(*)(Baz *)>(bar)); 

    這樣您就可以從重載集中明確選擇一個函數。

  • 如果允許修改foo2 ,您可以將其重寫為:

     template <class T, class R> auto foo2(R(*d)(T*)) {} 

    它或多或少是你以前擁有的,在這種情況下沒有decltype和你可以自由忽略的返回類型。
    實際上你不需要使用任何SFINAE函數來做到這一點,扣除就足夠了。
    在這種情況下foo2<Baz>(bar); 正確解決了。

這里的一般答案是: 表達式SFINAE在傳遞函數指針的類型上重載

對於實際情況,不需要使用類型特征或decltype() - 良好的舊重載decltype()將為您選擇最合適的函數並將其分解為“參數”和“返回類型”。 只列舉所有可能的調用約定

// Common functions
template <class T, typename R> void foo2(R(*)(T*)) {}

// Different calling conventions
#ifdef _W64
template <class T, typename R> void foo2(R(__vectorcall *)(T*)) {}
#else
template <class T, typename R> void foo2(R(__stdcall *)(T*)) {}
#endif

// Lambdas
template <class T, class D>
auto foo2(const D &d) -> void_t<decltype(d(std::declval<T*>()))> {}

將它們包裝在模板化結構中可能很有用

template<typename... T>
struct Foo2 {
    // Common functions
    template <typename R> static void foo2(R(*)(T*...)) {}
    ...
};
Zoo2<Baz>::foo2(bar);

雖然,它需要更多的成員函數代碼,因為它們有修飾符( constvolatile&&

暫無
暫無

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

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