template<typename T, typename... Ts>
void fooImpl(char const *cp, T val, Ts... args) { /* ... */ }
您正在声明原始模板的重载(!)。
既然在通话中
fooImpl(s.c_str(), args...);
there is a pack-expansion in the argument list, the unqualified-id denotes a dependent name1. Dependent name resolution applies. [temp.dep.candidate]:
对于依赖于模板参数的函数调用,
使用通常的查找规则(3.4.1,
3.4.2、3.4.3),但以下情况除外:
可以肯定地说,非限定名称查找不会找到第二个重载,因为它只考虑模板定义上下文中的声明。
ADL 确实适用于此,但全局命名空间不会与参数包中的任何类型关联。我们有std::string, char const*, double, int
。 [basic.lookup.argdep]/2 指定:
If T
是一个基本类型,它关联的命名空间集和
班级都是空的。
If T
是一个类类型(包括联合),其关联的类是:
班级本身;它所属的类别(如果有);和它的
直接和间接基类。其关联的命名空间是
其关联类是其成员的名称空间。此外,
如果T
是一个类模板专业化,它关联的命名空间和
类还包括: 与关联的名称空间和类
为模板类型参数提供的模板参数的类型
(不包括模板模板参数);其中任意的命名空间
模板模板参数是成员;以及其中任何一个类别
用作模板模板参数的成员模板是成员。
所以无论是基本类型还是std::string
包括全局命名空间作为关联命名空间。
长话短说...
...在 ADL 期间未搜索全局名称空间,并且未找到第二个重载。因此,函数模板的第一个重载是重载决议找到并随后选择的唯一重载。但未定义第一个重载,因此会发出链接器错误。
1) [temp.dep]/1:
在以下形式的表达式中:
postfix-expression (
expression-listopt )
其中后缀表达式是id-表达式, the
id-表达式表示一个附属名称 if
- 中的任何表达式表达式列表是一个包扩展 (14.5.3),
- [..]