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

时间:2014-06-10 20:00:19

标签: c++ templates polymorphism crtp

C ++中virtual的主要好处之一是能够使用基类(指针或引用)来调用派生方法。

我正在阅读using CRTP to implement static polymorphism,但我无法理解如何使用此技术实现我上面提到的内容,因为我无法将函数声明为采用类型Base这需要一个模板。

在我看来,文章中描述的内容可以通过简单地使用函数重载来实现,所以我确信这项技术必须有更多。

(PS:对this question的答案的评论中提到了这个确切的问题,但不幸的是没有人回复它:“vtables真正提供的是使用基类(指针或引用)来调用派生方法。你应该在这里展示如何使用CRTP。“)

这是我的最小代码,它给出错误“在'&'标记之前缺少模板参数  void 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);
}

3 个答案:

答案 0 :(得分:4)

嗯,您需要声明打印模板功能:

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

答案 1 :(得分:4)

感谢收到的评论和回答,我发布了我的实施内容,以防它对任何其他人都有用。

#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);
    }
}

答案 2 :(得分:3)

很抱歉,但CRTP确实没有这样做。这个想法通常是以一种非常特定于C ++的方式将一些代码注入依赖层次结构中。在您的示例中,您可以使用例如需要ToStringInterface()函数并使用CRTP将其绑定到现有类层次结构ToString()的接口:

class IStringable
{
    virtual string ToStringInterface() = 0;
};
class Unchangeable
{
    virtual string ToString();
};
template<class Derived>
class UnchangeableToIStringableMixin
{
    virtual string ToStringInterface()
    {
        return static_cast<Derived*>(this)->ToString();
    }
};
class StringableUnchangeable:
    public Unchangeable, UnchangeableToIStringableMixin<StringableUnchangeable>
{
};

但是,如果Unchangeable实际上可以更改,那么你就不会做那样的事情。别忘了考虑CRTP对于你正在做的事情不是正确的工具的可能性。