CRTP基类型使用的成员变量应该是派生类型吗?

时间:2014-07-30 23:18:05

标签: c++ crtp

我今天一直在学习CRTP(奇怪的重复模板模式),并相信我对它的理解很充分。

但是,在示例中,我已经看到状态存储在派生类型中,即使基类型依赖于它们的存在。对我来说,这似乎是不合逻辑的,成员变量应该在基本类型中,因为它的功能依赖于它们。

以下是我正在谈论的一个简单示例:

template <typename DerivedType>
class Base{
public:
    int calculate() {
        return static_cast<DerivedType&>(*this).x + static_cast<DerivedType&>(*this).y;
    }
};

class Derived : Base<Derived>{
public:
    int x; // ignore the fact that these aren't initialised for simplicity
    int y;
};

我的问题是:

我认为成员xy在基类型方面会更好吗?如果没有,为什么?

2 个答案:

答案 0 :(得分:8)

简短回答

这取决于你需要做什么,但如果不同的派生类提供不同类型的x, y,那么这些不能在基类中。

答案很长

继承的最常见用法是基类包括许多(多个)派生类中常见的内容。这个通用部分只编写一次并由每个派生类重用,从而使代码更短,更简洁,更易于维护等。在您的情况下,常见部分为calculate()

现在,无论此公共代码需要访问每个派生类专用的信息,都需要通过公共接口访问此信息。在您的示例中,此信息是成员x, y,每个派生类可能具有不同的类型。或者,它可以是成员函数x(), y()。这些函数可以采用不同类型(但数量相同)的参数,并且每个派生类具有不同的返回类型。

无论哪种方式,派生类的工作都是为异构信息提供通用接口。对于CRTP /静态多态,在成员函数的情况下,此公共接口仅仅是成员的名称和参数的数量。对于动态多态,相关机制是虚函数,公共接口包括函数的整个签名。

实际存储数据的位置无关紧要;这取决于很多事情。可能的情况是,数据毕竟存储在基类中,但仍然可以通过派生类中的成员函数访问它们。

一个示例是一个元组实现,其中base class实现了不同类型元组之间的所有常见功能,而一些tuple views是从此基础派生的,以模拟像flipping这样的操作元组元素的顺序,concatenating"zipping"元组在一起等等。请注意,所有这些视图都是惰性的,类似于std::reverse_iterator允许您以相反的顺序遍历序列而不实际操作的方式数据提前。

在这种情况下,基类的成员函数at()提供对元组元素的随机访问。这将调用派生类的call_at(),派生类又访问实际存储在基类中的数据。因此,每个派生类只知道每个元素的位置;使用此信息,基类实现所有剩余的功能(例如,产生新元组的operator[],其中每个元素是将operator[]应用于原始元组的相应元素的结果。

D'template mixins为CRTP提供了一种更方便,更简洁的替代方案;几乎和宏一样方便。在这种情况下,您的static_cast<DerivedType&>(*this).x和我的der().x只是x。另外,您根本不需要DerivedType内的Base

答案 1 :(得分:1)

我认为最好假设派生类有两个成员函数x()y()。您可以更改Base::calculate()的实现以使用这些函数,而不是使用成员变量。

然后,派生类在它拥有的数据类型中有更多的自由。