[英]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.