以下代码编译正常,但会产生链接器错误:
class Base {};
class Derived : public Base {};
template <typename T>
void f(const T& value);
template <>
void f(const Base& value) {
// ...
}
int main() {
Base b;
f(b);
Derived d;
f(d); // This line causes linker error.
return 0;
}
是否可以在不为派生类添加重复f()
专门化的情况下编译和链接此代码?
谢谢。
P.S我使用clang,Apple LLVM 6.0版(clang-600.0.56)(基于LLVM 3.5svn)。
P.P.S链接器错误是:
Undefined symbols for architecture x86_64:
"void f<Derived>(Derived const&)", referenced from:
_main in test2-4245dc.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
答案 0 :(得分:2)
假设您确实需要拥有模板并且必须具有Base
的所有衍生物来调用特化,您可以使用重载替换特化并禁用从Base
派生的所有类型的模板使用类型特征:
template <typename T, typename std::enable_if<!std::is_base_of<Base, T>::value>::type>
void f(const T& value);
// no template
void f(const Base& value)
这样,只有Base
的重载可用于派生类型。
答案 1 :(得分:2)
发生链接器错误是因为您尚未定义(仅声明)函数模板
template <typename T>
void f(const T& value);
为避免这种情况,请定义上面的模板,您的代码将会编译,但可能它仍然无法完成您想要的操作。
使用f
类型的参数调用Derived
时,与您的专业化相比,上述模板更匹配,因为后者需要派生到基础的转换,而前者不需要
只有在推导出的模板参数类型不是Base
或从{{1}派生的类型时,才能使用enable_if
来允许第一个模板参与重载解析,从而实现所需的行为}。
Base
然后更改另一个template <typename T>
typename std::enable_if<!std::is_base_of<Base, T>::value>::type
f(const T& value) {
}
,使其不是专业化,而只是过载。
f
通常,更喜欢重载功能模板而不是专业化。阅读this以了解功能模板专业化的缺陷。
答案 2 :(得分:1)
问题在于:
template <typename T>
void f(const T& value);
此函数未定义,但在调用
时,它是编译器的最佳匹配项f(d)
Derived d;
Base
专精template<> void f(const Base& value)
参与f(d)
的重载解析,但不如完整模板template <typename T> void f(const T& value)
更适合。
一个想法是完全删除专业化,只定义完整的模板,代码可能会做你想要的。
答案 3 :(得分:1)
与其他人所说的一样,你还没有定义通用模板函数,它是派生类型的最佳候选者。如果您想更好地理解模板函数重载解析的工作原理,请参考此参考:http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading
你的例子有点做作(也许是为了简洁起见)。但一般来说,如果需要为特定类型实现特殊逻辑,或者如果要在单独的源文件中定义模板类和函数,并且只需要/想要,则应该只提供模板特化支持某些类型。也许这就是你真正想要的东西,但是仿制药被称为仿制药。