将派生的 class 传递给专门使用基 class 的模板函数
Passing a derived class to a template function specialized with base class
以下代码可以正常编译,但会产生 linker 错误:
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;
}
是否可以在不为派生的 class 添加重复的 f()
特化的情况下编译和 link 代码?
谢谢。
P.S 我正在使用 clang,Apple LLVM 版本 6.0 (clang-600.0.56)(基于 LLVM 3.5svn)。
P.P.S link错误是:
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)
问题出在这里:
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)
.
合适
一个想法是完全删除专业化,只定义完整的模板,代码可能会做你想要的。
假设您确实需要模板并且必须具有 Base
的所有派生来调用特化,您可以用重载替换特化并禁用从 [=11 派生的所有类型的模板=] 使用类型特征:
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
的重载可用于派生类型。
发生链接器错误是因为您没有定义,只是声明了函数模板
template <typename T>
void f(const T& value);
为了避免它,定义上面的模板,你的代码将编译,但大概它仍然没有做你想要的。
当使用 Derived
类型的参数调用 f
时,与您的专业化相比,上述模板更匹配,因为后者需要派生到基础的转换,而前者不需要.
您可以通过使用 enable_if
实现您想要的行为,仅当推导的模板参数类型不是 Base
或派生自 [= 的类型时,才允许第一个模板参与重载决议16=].
template <typename T>
typename std::enable_if<!std::is_base_of<Base, T>::value>::type
f(const T& value) {
}
并更改另一个 f
所以它不是专业化,而是超载。
void f(const Base& value) {
// ...
}
一般来说,重载函数模板胜过特化。阅读 this 以了解函数模板特化的陷阱。
正如其他人所说,您还没有定义泛型模板函数,它是派生类型的最佳候选者。如果您想更好地了解模板函数重载决策的工作原理,请在此处查阅此参考资料:http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading
你的例子有点做作(可能是为了简洁起见)。但一般来说,如果您需要为特定类型实现特殊逻辑,或者如果您想要在单独的源文件中定义模板 class(es) 和函数,并且仅need/want 支持某些类型。也许这就是您真正想要的,但是泛型被称为泛型是有原因的。
以下代码可以正常编译,但会产生 linker 错误:
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;
}
是否可以在不为派生的 class 添加重复的 f()
特化的情况下编译和 link 代码?
谢谢。
P.S 我正在使用 clang,Apple LLVM 版本 6.0 (clang-600.0.56)(基于 LLVM 3.5svn)。
P.P.S link错误是:
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)
问题出在这里:
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)
.
一个想法是完全删除专业化,只定义完整的模板,代码可能会做你想要的。
假设您确实需要模板并且必须具有 Base
的所有派生来调用特化,您可以用重载替换特化并禁用从 [=11 派生的所有类型的模板=] 使用类型特征:
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
的重载可用于派生类型。
发生链接器错误是因为您没有定义,只是声明了函数模板
template <typename T>
void f(const T& value);
为了避免它,定义上面的模板,你的代码将编译,但大概它仍然没有做你想要的。
当使用 Derived
类型的参数调用 f
时,与您的专业化相比,上述模板更匹配,因为后者需要派生到基础的转换,而前者不需要.
您可以通过使用 enable_if
实现您想要的行为,仅当推导的模板参数类型不是 Base
或派生自 [= 的类型时,才允许第一个模板参与重载决议16=].
template <typename T>
typename std::enable_if<!std::is_base_of<Base, T>::value>::type
f(const T& value) {
}
并更改另一个 f
所以它不是专业化,而是超载。
void f(const Base& value) {
// ...
}
一般来说,重载函数模板胜过特化。阅读 this 以了解函数模板特化的陷阱。
正如其他人所说,您还没有定义泛型模板函数,它是派生类型的最佳候选者。如果您想更好地了解模板函数重载决策的工作原理,请在此处查阅此参考资料:http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading
你的例子有点做作(可能是为了简洁起见)。但一般来说,如果您需要为特定类型实现特殊逻辑,或者如果您想要在单独的源文件中定义模板 class(es) 和函数,并且仅need/want 支持某些类型。也许这就是您真正想要的,但是泛型被称为泛型是有原因的。