Cra*_*g P 3 c++ linker-errors variadic-templates
我有一个包含可变参数模板和辅助函数的程序:
#include <iostream>
#include <string>
using std::cout;
template<typename... Ts>
void fooImpl(char const *cp, Ts... args);
template<typename... Ts>
inline void foo(const std::string &s, Ts... args)
{
fooImpl(s.c_str(), args...);
}
void fooImpl(char const *cp)
{
// do something
}
template<typename T, typename... Ts>
void fooImpl(char const *cp, T val, Ts... args)
{
char special{'@'};
while (*cp)
{
if (*cp == special)
{
// handle val ...
// recurse over remaining args
fooImpl(cp, args...);
return;
}
++cp;
}
}
int main()
{
std::string s = "Hello!";
foo("Text", s, "C++", 3.14159, 42);
}
Run Code Online (Sandbox Code Playgroud)
这会产生链接器错误:
/tmp/ccZpPMC2.o:vt-test.cc:功能void foo<std::string, char const*, double, int>(std::string const&, std::string, char const*, double, int):错误:
未定义引用'void fooImpl<std::string, char const*, double, int>(char const*, std::string, char const*, double, int)'
如果我把之前的定义foo放下来,它编译并链接很好main().所以我认为它只是一个bug,但是我用GCC 4.9和clang 3.5得到了这个,所以也许我错过了什么?
template<typename T, typename... Ts>
void fooImpl(char const *cp, T val, Ts... args) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
您正在声明原始模板的重载(!).
自从在通话中
fooImpl(s.c_str(), args...);
Run Code Online (Sandbox Code Playgroud)
在参数列表中有一个包扩展,unqualified-id表示一个从属名称1.依赖名称解析适用.[temp.dep.candidate]:
对于依赖于模板参数的函数调用,使用通常的查找规则(3.4.1,3.4.2,3.4.3)找到候选函数,除了:
对于使用非限定名称查找(3.4.1)[..]的查找部分,仅查找模板定义上下文中的函数声明.
对于使用关联命名空间(3.4.2)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明.
可以肯定地说,非限定名称查找不会找到第二个重载,因为它只考虑模板定义上下文中的声明.
ADL确实适用于此处,但全局命名空间不会与参数包中的任何类型相关联.我们有std::string, char const*, double, int.[basic.lookup.argdep]/2指定:
如果
T是基本类型,则其关联的命名空间和类集都是空的.如果
T是类类型(包括联合),则其关联的类是:类本身; 它所属的成员,如果有的话; 及其直接和间接基类.其关联的名称空间是其关联类是成员的名称空间.此外,如果T是类模板特化,则其关联的名称空间和类还包括:与模板类型参数(模板模板参数除外)提供的模板参数类型相关联的名称空间和类; 任何模板模板参数都是成员的名称空间; 以及用作模板模板参数的任何成员模板的类都是成员.
因此,基本类型也不std::string包括全局命名空间作为关联的命名空间.
...在ADL期间未搜索全局命名空间,并且未找到第二个重载.因此,函数模板的第一个重载是唯一找到并随后通过重载决策选择的重载.但是,未定义第一个重载,因此会发出链接器错误.
在表达式中:
后缀表达式
(表达式列表选择)其中postfix-expression是一个id-expression, id-expression表示依赖名称 if
- 表达式列表中的任何表达式都是包扩展(14.5.3),
- [..]