[英]Inconsistent parameter pack deduction int and int& in variadic templated member function that creates a thread that runs a member function
當我嘗試運行這個玩具示例時,我得到一個不一致的參數包編譯器錯誤。 有人可以說明為什么'int a'被推斷為int&here? 在下面的示例中,當我使用int literal運行下面的'test'函數時,它可以正常工作。 在此先感謝您的解釋!
class Test {
public:
Test() {}
~Test() {
t.join();
}
void print(int num)
{
std::cout << num << std::endl;
}
template<class ...Args>
void test(void(Test::*b)(Args...) , Args&&... args)
{
t = std::thread(b, this, std::forward<Args>(args)...);
}
std::thread t;
};
int main()
{
int a = 123;
Test test;
test.test(&Test::print, a);
// test.test(&Test::print, 123); works
}
錯誤:
prog.cc: In function 'int main()':
prog.cc:82:40: error: no matching function for call to 'Test::test(
void (Test::*)(int), int&)'
test.test(&Test::print, a);
^
prog.cc:82:40: note: candidate is:
prog.cc:62:10: note: template<class ... Args> void Test::test(void
(Test::*)(Args ...), Args&& ...)
void test(void(Test::*b)(Args...) , Args&&... args)
^
prog.cc:62:10: note: template argument deduction/substitution failed:
prog.cc:82:40: note: inconsistent parameter pack deduction with 'int' and
'int&'
test.test(&Test::print, a);
^
切勿使用推導的前向引用類型與其他參數完全匹配。
完美轉發Args
,當你通過一個左int
推導Args
為int&
。 然后int& &&
折疊成int&
。
簡而言之, 永遠不要使用推導的前向引用類型來完全匹配其他參數 。
有一些罕見的例外,但這是在庫式代碼中,其中一個類型已經在早期的上下文中從另一個類推斷出來。
這個:
template<class ...Args>
void test(void(Test::*b)(Args...) , Args&&... args)
{
t = std::thread(b, this, std::forward<Args>(args)...);
}
受到限制。
嘗試:
template<class F, class ...Args>
void test(F&& f, Args&&... args)
{
t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...);
}
第一個arg究竟是什么不是你的問題。 它可以是指向this
的成員函數指針,它可以是一個可以將this
作為其第一個參數的對象。
如果由於某種原因你想要堅持第一個參數是成員函數指針:
template<class R, class...A0s, class ...Args>
void test(R(Test::*f)(A0s...), Args&&... args)
{
t = std::thread(f, this, std::forward<Args>(args)...);
}
不要過度約束它。 如果你真的想確保錯誤發生在test
調用而不是在它的體內,我們可以這樣做:
template<class R, class...A0s, class ...Args>
auto test(R(Test::*f)(A0s...), Args&&... args)
-> decltype((void)((std::declval<Test*>()->*f)(std::declval<typename std::decay<Args>::type>()...) )>
{
t = std::thread(f, this, std::forward<Args>(args)...);
}
我們SFINAE禁用此基於能夠調用this->*f
與args...
衰減副本args...
這通常是矯枉過正的。
最后,我們可以這樣做:
template<class T> struct tag_t{using type=T;};
template<class T> using no_deduction=typename tag_t<T>::type;
template<class ...Args>
void test(void(Test::*b)(Args...) , no_deduction<Args>... args)
{
t = std::thread(b, this, std::forward<Args>(args)...);
}
我們阻止對參數的推論,並且只對函數指針進行模式匹配。 如果b
想要參考,這不起作用; 我們需要一些額外的類型編程來將T&
變換為std::reference_wrapper<T>
:
template<class T>
struct compatible_arg { using type=T; };
template<class T>
struct compatible_arg<T&> {
using type=std::reference_wrapper<T>;
};
template<class T>
using compatible_arg_t = typename compatible_arg<T>::type;
template<class ...Args>
void test(void(Test::*b)(Args...) , compatible_arg_t<Args>... args)
{
t = std::thread(b, this, std::forward<decltype(args)>(args)...);
}
它將T&&
映射到T&&
, T
映射到T
和T&
to std::reference_wrapper<T>
。
但實際上,請停留在:
template<class F, class ...Args>
void test(F&& f, Args&&... args)
{
t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...);
}
template<class ...Args>
void test(void(Test::*b)(Args...) , Args&&... args)
{
t = std::thread(b, this, std::forward<Args>(args)...);
}
當你做這樣的事情時,它意味着:
Args...
來自作為第一個參數傳遞的PMF。 Args...
。 這幾乎不是你真正想做的事情。 函數參數的類型與其對應參數的類型和值類別之間通常沒有精確匹配關系。
在這里,您實際上並不需要PMF的參數類型(更不用說您必須編寫大量重載以涵蓋cv-和ref-qualifiers的所有可能組合),因此您可以將其限制為“指向成員的指針”某種類型的Test
“:
template<class F, class... Args>
void test(F Test::* b, Args&&... args)
{
t = std::thread(b, this, std::forward<Args>(args)...);
}
或者只是讓它不受約束:
template<class F, class... Args>
void test(F&& f, Args&&... args)
{
t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...);
}
或者,您可以引入一個新包:
template<class ...Args, class... Args2>
void test(void(Test::*b)(Args...) , Args2&&... args)
{
t = std::thread(b, this, std::forward<Args2>(args)...);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.