Is it possible to override a variadic template by changing the number of fixed parameters before the function parameter pack? For example:
#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);
}
Running this causes the second foo
to be called, but I want the first to be called. Is there a better way to overload this function?
Second variant is selected because it does not involve an extra conversion required to create an instance of std::string
for the last argument. If you call class constructors explicitly (or adjust arguments to take exactly what you pass) then it will work fine:
foo(std::string{"key"}, std::string{"msg"}, std::string{"data"}, 1, 2); // THREE STRINGS
This is because a string literal is not of type std::string
. It is of type const char[N]
. So the type of the third parameter of the function instantiated from the second function template is deduced to be const char*
, which is considered a better match than the function instantiated from the first function template.
You can change the type of the parameters from std::string
to const char*
, or use explicit conversions suggested by VTT's answer.
As explained by VTT and xskxzr, "data"
is a string literal, so is a const char [5]
, so is convertible to but not exactly a std::string
, so a generic template type is a better match than a std::string
and the compiler prefer the first version.
You can choose the first version of foo()
passing a std::string{"data"}
but, if you don't want to change the call, another possible solution is SFINAE enable/disable the second version of foo()
.
I mean... if you write a fIsCToS
(as "first is convertible to string") custom type traits as follows
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>
{ };
you can rewrite the second version of foo()
using it as follows
template <typename ...Args>
typename std::enable_if<false == fIsCToS<Args...>{}>::type
foo(std::string, std::string, Args...)
{ std::cout << "TWO STRINGS\n"; }
The following is your modified example
#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
}
If you can use C++14, you can use std::enable_if_t
instead of typename std::enable_it<...>::type
so the second foo()
can be simplified as
template <typename ...Args>
std::enable_if_t<false == fIsCToS<Args...>{}>
foo(std::string, std::string, Args...)
{ std::cout << "TWO STRINGS\n"; }
OK, so I came up with a different solution. I hate answering my own question, but this is sort of the answer I was looking for and maybe it can help others.
What I did instead was to not overload the templated function and instead combine the entire signature into a single parameter pack. Then, I overloaded a non-templated function that does the actual work, using std::forward
to forward the args to one of these overloaded functions. In this way, I let the compiler decide which method to call.
My solution:
#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);
}
See it running here :
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.