![](/img/trans.png)
[英]C++ partial template argument deduction for function with variadic pack produces ambiguous call in Clang and MSVC
[英]Variadic helper function with partial argument pack
在以下代碼中:
#include <iostream>
struct Base {
virtual ~Base() = default;
template <typename T, typename... Args> void helper (void (T::*)(Args..., int), Args...);
void bar (int n) {std::cout << "bar " << n << std::endl;}
};
struct Derived : Base {
void baz (double d, int n) {std::cout << "baz " << d << ' ' << n << std::endl;}
};
template <typename T, typename... Args>
void Base::helper (void (T::*f)(Args..., int), Args... args) {
// A bunch on lines here (hence the motivation for the helper function)
for (int n = 0; n < 5; n++)
(dynamic_cast<T*>(this)->*f)(args..., n);
// ...
}
int main() {
Base b;
Derived d;
b.helper(&Base::bar); // GCC 4.8.1 will accept this, Visual Studio 2013 won't.
d.helper<Derived, double>(&Derived::baz, 3.14); // Visual Studio 2013 will accept this, GCC 4.8.1 won't
}
我無法通過GCC4.8.1或VS2013來編譯上面的兩行。 他們只會編譯一個而不會編譯另一個(並且他們不同意哪一行是正確的也不正確)。 兩個編譯器都錯誤消息指出模板扣除失敗。 那究竟是什么錯? 我已將所有模板參數放在最后一行(我認為可以推斷),但它仍然無法由GCC推斷,盡管VS可以。 然而,VS無法推斷出b.foo(&Base::bar);
的模板參數b.foo(&Base::bar);
當我為其放置模板參數時,GCC可以在沒有任何模板參數的情況下推導出它們。 這里完全不知所措。 兩個編譯器都在這里竊聽? 程序員的任何修復可能嗎?
參數包必須放在參數列表的末尾才能自動推導出來。
編譯器不能從給定的參數列表中推導出(Args..., int)
(int, Args...)
而是使用(int, Args...)
而程序將編譯。
#include <iostream>
struct Base {
virtual ~Base() = default;
template <typename T, typename... Args> void helper (void (T::*)(int, Args...), Args...);
void bar (int n) {std::cout << "bar " << n << std::endl;}
};
struct Derived : Base {
void baz (int n, double d) {std::cout << "baz " << d << ' ' << n << std::endl;}
};
template <typename T, typename... Args>
void Base::helper (void (T::*f)(int, Args...), Args... args) {
// A bunch on lines here (hence the motivation for the helper function)
for (int n = 0; n < 5; n++)
(dynamic_cast<T*>(this)->*f)(n, args...);
// ...
}
int main() {
Base b;
Derived d;
b.helper(&Base::bar);
d.helper<Derived, double>(&Derived::baz, 3.14);
}
如果你必須在參數列表的末尾添加int
,你可以使用identity
技巧,如@Barry所說。
准系統identity
實現可以很簡單:
template<typename T>
struct identity {
typedef T type;
};
然后您可以手動推導參數類型:
template <typename T, typename... Args>
void Base::helper (void (T::*f)(typename identity<Args>::type..., int), typename identity<Args>::type... args) {
// A bunch on lines here (hence the motivation for the helper function)
for (int n = 0; n < 5; n++)
(dynamic_cast<T*>(this)->*f)(args..., n);
// ...
}
b.helper<Base>(&Base::bar);
d.helper<Derived, double>(&Derived::baz, 3.14);
我認為這兩個調用都是無效的,因為它們都涉及非推斷的上下文。 從§14.8.2.5開始:
未推斷的上下文是:
- [..]
- 函數參數包,它不會出現在參數聲明列表的末尾。
如果以包含非推導上下文的方式指定類型名稱,則包含該類型名稱的所有類型也是非推斷的。
如果你有void (T::*f)(Args..., int)
,那就是在非推導的上下文中,因為函數內部的函數參數包最后沒有出現。 指向成員的參數列表是非推導的這一事實使得整個函數調用是非推導的。 因此無法推斷出此調用:
b.helper(&Base::bar);
對於第二個,即使看起來你明確指定了Args...
,參數void (T::*f)(Args..., int)
仍然在非推導的上下文中,因此編譯器具有無法知道是否需要更多的 Args
。
因此,一種解決方案是通過使用,例如,向后使用身份技巧來迫使該論證不必推斷:
template <typename T, typename... Args>
void foo (void (T::*)(typename identity<Args>::type..., int), Args...);
這樣,這兩行都編譯:
b.helper(&Base::bar);
d.helper<Derived, double>(&Derived::baz, 3.14);
雖然現在你必須確保你得到Args...
如果你沒有明確指定它。
我不會將第一個參數寫為成員函數的指針。
在你的特定情況下,它需要將第一個Args...
放入一個非推導的上下文中 - 標准很明顯是關於之后應該發生的事情的泥濘,特別是考慮到[temp.deduct.call] / p1中的規則那
當函數參數包出現在非推導的上下文(14.8.2.5)中時,永遠不會推導出該參數包的類型。
當你編寫void (T::*)(typename identity<Args>::type..., int)
時,我不知道這個規則的含義是什么。 編譯器也不同意。
即使在正常情況下,你也必須編寫12個重載以匹配所有可能形式的成員函數指針(4個可能的cv-qualifier-seq s乘以3個可能的ref-qualifiers )。 在你的情況下,跳過一些(例如volatile
和&&
)可能是安全的,但它仍然是煩人的代碼重復。 此外,如果你在推導的上下文中使用Args...
兩次,它們是獨立推導出來的,推導出的類型必須完全匹配,這對最終用戶來說可能會變得混亂。 ( std::max(1, 2.5)
,有人嗎?)
相反,我會把它寫成指向成員的指針:
template <typename T, typename... Args, typename R>
void Base::helper (R T::*f, Args... args) {
// A bunch of lines here (hence the motivation for the helper function)
for (int n = 0; n < 5; n++)
(dynamic_cast<T*>(this)->*f)(args..., n);
// ...
}
RT::*
匹配所有指向成員的指針; 當您將指針傳遞給成員函數時, R
被推斷為函數類型。 如果要強制執行R-must-a-function,可以在std::is_function<R>::value
上使用static_assert
。
演示 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.