[英]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
的類型。
但是,除了一個版本的bar
, foo2
通過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);
雖然,它需要更多的成員函數代碼,因為它們有修飾符( const
, volatile
, &&
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.