CRTP的静态多态性:使用基类调用派生方法

MGA*_*MGA 5 c++ polymorphism templates crtp

virtualC ++ 的主要优点之一是能够使用基类(指针或引用)来调用派生方法。

我正在阅读使用CRTP实现静态多态性的知识,但是我不明白如何使用该技术实现上面提到的内容,因为Base当需要模板时,我无法将函数声明为采用类型。

在我看来,仅通过使用函数重载就可以实现本文中描述的内容,因此,我敢肯定,该技术必须有更多的功能。

(PS:在回答该问题的评论中提到了这个确切的问题,但不幸的是没有人回答:“ vtables真正提供的功能是使用基类(指针或引用)来调用派生方法。您应该在此处显示如何使用CRTP进行操作。”)

这是我的最小代码,它给出错误“在'&'令牌无效Print(Base&Object)之前缺少模板参数”。

#include <cstring>
#include <iostream>

template <typename Derived>
struct Base
{
    std::string ToStringInterface() { return static_cast<Derived*>(this)->ToString(); }

    std::string ToString()  {   return "This is Base.";     }
};

struct Derived : Base<Derived>
{
    std::string ToString()  {   return "This is Derived.";  }
};

void Print(Base& Object)
{
    std::cout << Object->ToStringInterface() << std::endl;
}

int main()
{
    Derived MyDerived;

    // This works, but could have been achieved with a function overload.
    std::cout << MyDerived.ToStringInterface() << std::endl;

    // This does not work.
    Print(MyDerived);
}
Run Code Online (Sandbox Code Playgroud)

Kir*_*xas 5

好吧,你需要声明 print 一个模板函数:

template<class T>
void Print(Base<T>& Object)
{
    std::cout << Object.ToStringInterface() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

  • 您可以创建一个“Base&lt;Whatever&gt;”类型的实例,它实际上不是“Whatever”,但调用“ToStringInterface()”会调用未定义的行为。使构造函数受到保护和/或检查“ToStringInterface()”中的正确类型。请注意,使用“dynamic_cast”进行检查并不总是可能的,因为 CRTP 不一定涉及任何虚拟函数,但这些函数是此类强制转换所必需的。 (3认同)

MGA*_*MGA 5

感谢收到的评论和答案,我正在发布我的实现,以防它对其他人有用。

#include <cstring>
#include <iostream>

template <typename Derived>
class Base
{
public:
    std::string ToStringInterface()
    {
        return static_cast<Derived*>(this)->ToString();
    }
};

template<>
class Base<void> : public Base<Base<void> >
{
public:
    std::string ToString()
    {
        return "This is Base (default implementation).";
    }
};

class Derived : public Base<Derived>
{
public:
    std::string ToString()
    { 
        return "This is Derived.";
    }
};

template <typename T>
void Print(Base<T>& Object)
{
    std::cout << Object.ToStringInterface() << std::endl;
}

int main()
{   
    int Decision;
    std::cout << "Do you want to create an object of type Base (input 0) or Derived (input 1)? ";
    std::cin >> Decision;
    if (Decision == 0)
    {
        Base<void> MyBase;
        Print(MyBase);
    }
    else
    {
        Derived MyDerived;
        Print(MyDerived);
    }
}
Run Code Online (Sandbox Code Playgroud)