如何强制编译器为基类选择模板函数重载?
这是一个说明问题的例子
#include <iostream>
class A
{};
class B : public A
{};
template <class T>
void f (const T& t)
{
std::cout << "Generic f" << std::endl;
}
void f (const A& a)
{
std::cout << "Overload for A" << std::endl;
}
template <class T>
void call_f (const T& t)
{
f (t);
}
int main()
{
call_f (10);
call_f (A());
call_f (B());
return 0;
}
它产生输出
Generic f
Overload for A
Generic f
为什么编译器在第三种情况下没有选择f (const A&)
? UPD :好的,这个明确void f<B> (const B&)
优于void f (const A&)
,但我仍在寻找第二个问题的答案。
是否有可能强制它而不将B投射到A ?
答案 0 :(得分:3)
使用call_f(B())
会导致调用`f(),这与模板版本最匹配。对于非模板版本,需要进行转换。结果,选择了模板。如果模板和非模板同样是好的选项,那么非模板将是首选。
如果您想要调用非模板,则需要使模板成为非选项。例如,模板可以像
一样实现#include <type_traits>
template <class T>
typename std::enable_if<!std::is_base_of<A, T>::value>::type f(T const&)
{
std::cout << "Generic f\n";
}
如果无法使用C ++ 11,您可以实现std::is_base_of<...>
版本,使用Boost版本或使用简单发送:
struct true_type {};
struct false_type {};
true_type A_is_base_of(A const*) { return true_type(); }
false_type A_is_base_of(void const*) { return false_type(); }
template <class T>
void f (T const&, false_type)
{
std::cout << "Generic f\n";
}
void f (A const&, true_type)
{
std::cout << "Overload for A\n";
}
template <class T>
void call_f (const T& t)
{
f (t, A_is_base_of(&t));
}
答案 1 :(得分:2)
我认为这实际上是可能的。诀窍是利用重载决策更喜欢C风格的可变参数函数参数这一事实。这样我们就可以通过为我们构建适当的标记来创建支持标记分派的辅助函数。辅助函数的重载分辨率迫使编译器从候选函数列表中删除通用模板函数,只留下专用函数。
使用代码可以更加清晰,所以让我们来看看。
#include <iostream>
struct foo {};
struct bar : public foo {};
struct generic_tag {};
struct foo_tag {};
generic_tag make_tag(...) {
return generic_tag();
}
foo_tag make_tag(foo const *) {
return foo_tag();
}
template<typename T>
void f(T const &t, generic_tag) {
std::cout << "Generic" << std::endl;
}
void f(foo const &f, foo_tag) {
std::cout << "Specialized" << std::endl;
}
template<typename T>
void call_f(T const &t) {
// This is the secret sauce. The call to make_tag(t) will always
// select the most specialized overload of make_tag() available.
// The generic make_tag() will only be called if the compiler can't
// find some other valid version of make_tag().
f(t, make_tag(&t));
}
int main() {
call_f( 10); // Prints "Generic"
call_f(foo()); // Prints "Specialized"
call_f(bar()); // Prints "Specialized"
}
我使用GCC 4.8.2验证了此解决方案适用于Ununtu 14.04。
答案 2 :(得分:0)
假设您知道您正在调用call_f
的内容将始终是A
的派生类型,您可以简单地明确询问该版本的模板,如下所示:
call_f<A> (B());
如果您实际使用不可转换为A
的类型调用它,就像第三类
class C
{};
你应该得到一个解决这个问题的编译器错误(沿着“错误C2664:'call_f':不能将参数1从'C'转换为'const A&amp;”)