为什么编译器选择这个模板函数而不是重载的非模板函数?

Why is the compiler choosing this template function over an overloaded non-template function?

本文关键字:函数 重载 选择 编译器 为什么      更新时间:2023-10-16

使用vc++ 2010,给出如下:

class Base { };
class Derived : public Base { };
template<class T> void foo(T& t);  // A
void foo(Base& base);              // B
Derived d;
foo(d);                            // calls A
foo(static_cast<Base&>(d));        // calls B

我想在上面调用"B"。我可以通过转换到Base来实现这一点,但为什么这是必要的?

我希望对所有不是从Base派生的类型(内置类型等)调用模板函数,但我希望对从Base派生的类型调用非模板重载,而不需要客户端显式强制转换。我也尝试过使重载成为模板的专门化,但在这种情况下会发生相同的行为。得到我想要的东西的惯用方法是什么?

在所有条件相同的情况下,非模板函数优于函数模板。然而,在您的场景中,并非所有事情都是相等的:(A)与T = Derived完全匹配,但是(B)需要将参数从派生到基的转换。

您可以通过使用SFINAE(替换失败不是错误)来解决特定情况下的这个问题,以防止(A)被实例化为从Base派生的类型:

#include <type_traits>
#include <utility>
template <typename T>
typename std::enable_if<
    !std::is_base_of<Base, T>::value
>::type foo(T& x)
{
}
void foo(Base& x)
{
}

选择模板版本是因为当使用类型为Derived的参数调用时,它比重载版本更匹配。您可以使用SFINAE从重载解析中删除模板版本,以便在使用BaseDerived类型的参数调用时选择其他版本。

#include <type_traits>
#include <iostream>
class Base { };
class Derived : public Base { };
template<class T> 
typename std::enable_if<
  std::is_base_of<Base, T>::value == false
>::type
foo(T&)  
{ 
  std::cout << "template foo" << std::endl; 
}

void foo(Base&)
{ 
  std::cout << "non-template foo" << std::endl; 
}

int main()
{
  Derived d;
  Base b;
  foo( d );
  foo( b );
}