[英]Overload variadic template's fixed parameters
是否可以通過在函數參數包之前更改固定參數的數量來覆蓋可變參數模板? 例如:
#include <iostream>
template <typename ...Args>
void foo(std::string, std::string, std::string, Args...)
{
std::cout << "THREE STRINGS\n";
}
template <typename ...Args>
void foo(std::string, std::string, Args...)
{
std::cout << "TWO STRINGS\n";
}
int main() {
foo("key", "msg", "data", 1);
}
運行它會導致第二個foo
被調用,但我希望第一個被調用。 有沒有更好的方法來重載此功能?
選擇第二個變體是因為它不涉及為最后一個參數創建std::string
實例所需的額外轉換。 如果你明確地調用類構造函數(或調整參數以完全接受你傳遞的),那么它將正常工作:
foo(std::string{"key"}, std::string{"msg"}, std::string{"data"}, 1, 2); // THREE STRINGS
這是因為字符串文字不是std::string
類型。 它的類型為const char[N]
。 因此,從第二個函數模板實例化的函數的第三個參數的類型被推導為const char*
,這被認為是比從第一個函數模板實例化的函數更好的匹配。
您可以將參數類型從std::string
更改為const char*
,或使用VTT答案建議的顯式轉換。
正如VTT和xskxzr所解釋的那樣, "data"
是一個字符串文字,因此是一個const char [5]
,所以可以轉換為但不完全是std::string
,因此通用模板類型比std::string
更好匹配std::string
和編譯器更喜歡第一個版本。
您可以選擇傳遞std::string{"data"}
的foo()
的第一個版本,但是,如果您不想更改調用,另一個可能的解決方案是SFINAE啟用/禁用第二個版本的foo()
。
我的意思是...如果你寫一個fIsCToS
(作為“首先是可轉換為字符串”)自定義類型特征如下
template <typename...> // for empty `Args...` list case
struct fIsCToS : std::false_type
{ };
template <typename T0, typename ... Ts>
struct fIsCToS<T0, Ts...> : std::is_convertible<T0, std::string>
{ };
您可以使用它重寫第二個版本的foo()
,如下所示
template <typename ...Args>
typename std::enable_if<false == fIsCToS<Args...>{}>::type
foo(std::string, std::string, Args...)
{ std::cout << "TWO STRINGS\n"; }
以下是您修改過的示例
#include <iostream>
template <typename...>
struct fIsCToS : std::false_type
{ };
template <typename T0, typename ... Ts>
struct fIsCToS<T0, Ts...> : std::is_convertible<T0, std::string>
{ };
template <typename ...Args>
void foo(std::string, std::string, std::string, Args...)
{ std::cout << "THREE STRINGS\n"; }
template <typename ...Args>
typename std::enable_if<false == fIsCToS<Args...>{}>::type
foo(std::string, std::string, Args...)
{ std::cout << "TWO STRINGS\n"; }
int main ()
{
foo("key", "msg", "data", 1, 2); // now print THREE STRINGS
}
如果你可以使用C ++ 14,你可以使用std::enable_if_t
而不是typename std::enable_it<...>::type
這樣第二個foo()
可以簡化為
template <typename ...Args>
std::enable_if_t<false == fIsCToS<Args...>{}>
foo(std::string, std::string, Args...)
{ std::cout << "TWO STRINGS\n"; }
好的,所以我想出了一個不同的解決方案。 我討厭回答我自己的問題,但這是我正在尋找的答案,也許它可以幫助別人。
我所做的是不重載模板化函數,而是將整個簽名組合到一個參數包中。 然后,我重載了一個執行實際工作的非模板化函數,使用std::forward
forward將args轉發給其中一個重載函數。 這樣,我讓編譯器決定調用哪個方法。
我的解決方案
#include <iostream>
void print_call(std::string key, std::string msg, std::string data, int)
{
std::cout << "THREE STRINGS\n";
}
void print_call(std::string key, std::string msg, int)
{
std::cout << "TWO STRINGS\n";
}
template <typename ...Args>
void foo(Args &&...args)
{
print_call(std::forward<Args>(args)...);
}
int main() {
foo("key", "msg", 1);
foo("key", "msg", "data", 1);
}
看到它在這里運行:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.