As explained by VTT and xskxzr, "data" is a string literal, so it is const char [5] , so it is converted to, but not exactly, to std::string , so the general type of the template is better compared to std::string and the compiler prefers the first version.
You can select the first version of foo() passing std::string{"data"} , but if you do not want to change the call, another possible solution is to enable / disable the second version of foo() SFINAE.
I mean ... if you write fIsCToS (as "the first option to convert to string"), as shown below
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"; }
Below 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"; }