C++ 为什么可以';模板函数将指向派生类的指针解析为指向基类的指针

C++ 为什么可以';模板函数将指向派生类的指针解析为指向基类的指针,c++,templates,inheritance,pointers,template-function,C++,Templates,Inheritance,Pointers,Template Function,编译器在编译时是否无法获取指向派生类的指针并知道它有基类?根据以下测试,它似乎无法。有关问题发生的位置,请参见我在末尾的评论 我如何才能让它工作? std::string nonSpecStr = "non specialized func"; std::string const specStr = "specialized func"; std::string const nonTemplateStr = "non template func"; class Base {}; class D

编译器在编译时是否无法获取指向派生类的指针并知道它有基类?根据以下测试,它似乎无法。有关问题发生的位置,请参见我在末尾的评论

我如何才能让它工作?

std::string nonSpecStr = "non specialized func";
std::string const specStr = "specialized func";
std::string const nonTemplateStr = "non template func";

class Base {};
class Derived : public Base {};
class OtherClass {};


template <typename T> std::string func(T * i_obj)
{ return nonSpecStr; }

template <> std::string func<Base>(Base * i_obj)
{ return specStr; }

std::string func(Base * i_obj)
{ return nonTemplateStr; }

class TemplateFunctionResolutionTest
{
public:
    void run()
    {
        // Function resolution order
        // 1. non-template functions
        // 2. specialized template functions
        // 3. template functions
        Base * base = new Base;
        assert(nonTemplateStr == func(base));

        Base * derived = new Derived;
        assert(nonTemplateStr == func(derived));

        OtherClass * otherClass = new OtherClass;
        assert(nonSpecStr == func(otherClass));


        // Why doesn't this resolve to the non-template function?
        Derived * derivedD = new Derived;
        assert(nonSpecStr == func(derivedD));
    }
};
std::string nonSpecStr=“non specialized func”;
std::string const specStr=“specialized func”;
std::string const nonTemplateStr=“non-template func”;
类基{};
派生类:公共基{};
类其他类{};
模板标准::字符串函数(T*i_obj)
{返回非specstr;}
模板标准::字符串函数(基本*i_对象)
{返回specStr;}
std::string func(基本*i_obj)
{返回非模板str;}
类TemplateFunctionResolutionTest
{
公众:
无效运行()
{
//函数解析顺序
//1.非模板函数
//2.专用模板功能
//3.模板功能
基地*基地=新基地;
断言(nonTemplateStr==func(base));
基础*衍生=新衍生;
断言(非模板str==func(派生));
OtherClass*OtherClass=新的OtherClass;
断言(nonSpecStr==func(otherClass));
//为什么这不能解析为非模板函数?
派生*derivedD=新派生;
断言(nonSpecStr==func(derivedD));
}
};
这不会像您所期望的那样解析为非模板函数,因为要这样做,必须执行从
Derived*
Base*
的转换;但模板版本不需要此强制转换,这导致后者在重载解析期间更匹配

要强制模板函数不同时匹配
Base
Derived
,可以使用SFINAE拒绝这两种类型

#include <string>
#include <iostream>
#include <type_traits>
#include <memory>

class Base {};
class Derived : public Base {};
class OtherClass {};

template <typename T> 
typename std::enable_if<
    !std::is_base_of<Base,T>::value,std::string
  >::type
  func(T *)
{ return "template function"; }

std::string func(Base *)
{ return "non template function"; }

int main()
{
  std::unique_ptr<Base> p1( new Base );
  std::cout << func(p1.get()) << std::endl;

  std::unique_ptr<Derived> p2( new Derived );
  std::cout << func(p2.get()) << std::endl;

  std::unique_ptr<Base> p3( new Derived );
  std::cout << func(p3.get()) << std::endl;

  std::unique_ptr<OtherClass> p4( new OtherClass );
  std::cout << func(p4.get()) << std::endl;
}
这不会像您所期望的那样解析为非模板函数,因为要这样做,必须执行从
Derived*
Base*
的转换;但模板版本不需要此强制转换,这导致后者在重载解析期间更匹配

要强制模板函数不同时匹配
Base
Derived
,可以使用SFINAE拒绝这两种类型

#include <string>
#include <iostream>
#include <type_traits>
#include <memory>

class Base {};
class Derived : public Base {};
class OtherClass {};

template <typename T> 
typename std::enable_if<
    !std::is_base_of<Base,T>::value,std::string
  >::type
  func(T *)
{ return "template function"; }

std::string func(Base *)
{ return "non template function"; }

int main()
{
  std::unique_ptr<Base> p1( new Base );
  std::cout << func(p1.get()) << std::endl;

  std::unique_ptr<Derived> p2( new Derived );
  std::cout << func(p2.get()) << std::endl;

  std::unique_ptr<Base> p3( new Derived );
  std::cout << func(p3.get()) << std::endl;

  std::unique_ptr<OtherClass> p4( new OtherClass );
  std::cout << func(p4.get()) << std::endl;
}

我认为你想要实现的不是一个好的编程风格。你使用的语言太微妙了。应用程序代码不应是编译器测试。Prætorian说的是对的,但这对所有编译器都有效吗?@KirillKobelev Overload resolution是一系列定义明确的步骤。任何行为不同的编译器都是不一致的。@KirillKobelev这是非常基本的东西。即使对于简单的程序,任何一个没有正确使用这个函数的编译器都是可用的。”
template std::string func
“你为什么要尝试编写模板函数专门化?它们有一些非常具体的用途,而这不是其中之一。@curiousguy这个问题很简单,因为我希望它清晰、容易回答,没有一大堆无关的细节。我对模板专门化、重载函数等的实际需求比问题所能显示的要复杂一些。我认为您想要实现的不是一种好的编程风格。你使用的语言太微妙了。应用程序代码不应是编译器测试。Prætorian说的是对的,但这对所有编译器都有效吗?@KirillKobelev Overload resolution是一系列定义明确的步骤。任何行为不同的编译器都是不一致的。@KirillKobelev这是非常基本的东西。即使对于简单的程序,任何一个没有正确使用这个函数的编译器都是可用的。”
template std::string func
“你为什么要尝试编写模板函数专门化?它们有一些非常具体的用途,而这不是其中之一。@curiousguy这个问题很简单,因为我希望它清晰、容易回答,没有一大堆无关的细节。我对模板专门化、重载函数等的实际需求比问题所能显示的要复杂一些。是否有办法限制通用模板函数,例如指定模板参数仅适用于数值对象?如果我可以这样做,那么派生*将不会解析为通用模板函数,因为它不是数字,因此将解析为我希望它解析的函数。类型特征能帮上忙吗?@ChrisMorris更新了答案“做一个演员”conversion@Prætorian不幸的是,我的编译器不支持enable_if或unique_ptr指令。我还有另一个问题,这个问题是在:@ChrisMorris
enable_如果你自己创建的
很小,那么显示了一个可能的实现。至于
unique\u ptr
这与你的问题并不相关,我只是用它来避免
删除对象。有没有办法限制泛型模板函数,比如指定模板参数仅适用于数字对象?如果我可以这样做,那么派生*将不会解析为通用模板函数,因为它不是数字,因此将解析为我希望它解析的函数。类型特征能帮上忙吗?@ChrisMorris更新了答案“做一个演员”conversion@Prætorian不幸的是,我的编译器不支持enable_if或unique_ptr指令。我还有另一个问题,这个问题是在:@ChrisMorris
enable_如果你自己创建的
很小,那么显示了一个可能的实现。至于
unique\u ptr
它与您的问题并不相关,我只是用它来避免
删除对象。
non template function
non template function
non template function
template function