使用继承类从模板类继承

时间:2017-12-08 18:02:08

标签: c++ templates inheritance interface

当我从类继承时,编译器必须知道基类的定义才能创建它。但是当我使用自己继承模板类(继承类)时,编译器如何创建代码?它还不知道班级的大小。

#include <iostream>

template <class T> class IFoo
{
public:
     virtual T addX(T foo, double val) = 0;
     // T memberVar; // uncomment for error
};

class Foo : public IFoo<Foo>
{
public:
    Foo(double value)
        : m_value(value) {}

    Foo addX(Foo foo, double b) override 
    { 
        return Foo(foo.m_value + b); 
    }

    double m_value;
};

int main()
{
    Foo foo1(1);
    Foo foo2 = foo1.addX(foo1, 1);

    std::cout << foo2.m_value;
}

首先我认为它有效,因为它是一个界面,但它也适用于普通类。

当我将模板存储为成员时,我得到一个错误,即Foo未定义,就像我预期的那样。

3 个答案:

答案 0 :(得分:2)

通过template class IFoo的定义,编译器无需知道Foo的大小IFoo<Foo>

在这种情况下,

Foo将是一个不完整的类(不是&#34;未定义&#34;或&#34;未声明&#34;)并且可以使用任何不完整类型的方式。出现在成员函数参数列表中就可以了。将成员变量声明为Foo*很好。禁止将成员变量声明为Foo(需要完整类型)。

答案 1 :(得分:2)

此处的一般概念称为奇怪的重复模板模式 CRTP 。搜索它将获得大量的点击。见:https://stackoverflow.com/questions/tagged/crtp

然而,有一个简单的解释,可能会回答你的问题而不会过多地使用CRTP。 C和C ++允许以下内容:

struct foo {
    struct foo *next;
    ...
};

或有两种类型:

struct foo;
struct bar;

struct foo {
    struct bar *first;
    ...
};

struct bar {
    struct foo *second;
    ...
};

只要使用指向structclass的指针,就不必提供该类型的完整定义。可以通过各种方式在此基础上对模板进行分层,并且必须清楚地分别说明参数化模板的类型及其在模板中的使用。添加SFINAE(替换失败不是错误),甚至可以制作不实例化的模板,因为无法使用给定类型完成任务。

答案 2 :(得分:1)

  

编译器如何创建代码?

回答这个问题与回答这个问题是一样的:编译器如何编译那个

struct Type;
Type func(Type);

Live example

如何定义不存在的类型并声明使用该类型的函数?

答案很简单:没有编译的代码实际上使用那个不存在的类型。由于没有可编译的代码,它甚至怎么会失败?

现在也许您想知道与您的代码有什么关系?如何使类可以将自身作为模板参数发送给它的父级?

让我们分析编译器在您执行此操作时看到的内容:

struct Foo : IFoo<Foo> { /* ... */ };

首先,编译看到了这个:

struct Foo ...

编译器现在知道Foo存在,但它是一个不完整的类型。

现在,他看到了:

... : IFoo<Foo> ...

它知道IFoo是什么,它知道Foo是一种类型。编译器现在只需要使用该类型实现IFoo

template <class T> struct IFoo
{
     virtual T addX(T foo, double val) = 0;
};

实际上,它声明了一个类,其中包含一个函数声明。您在上面看到声明具有不完整类型的函数有效。这里也是如此。此时,您的代码是可能的,因为此代码是:

struct Foo;
template struct IFoo<Foo>; // instanciate IFoo with Foo

那真的没有巫术。

现在让我们有一个更有说服力的例子。那怎么样?

template<typename T>
struct IFoo {
    void stuff(T f) {
        f.something();
    }
};

struct Foo : IFoo<Foo> {
    void something() {}
};

编译器如何在不完整类型上调用something

事情是:事实并非如此。当我们使用Foo时,something已完成。这是因为模板函数仅在使用时才会被实例化。

请记住,即使使用模板,我们也可以将函数定义分开吗?

template<typename T>
struct IFoo {
    void stuff(T f);
};

template<typename T>
void IFoo<T>::stuff(T f) {
    f.something();
}

struct Foo : IFoo<Foo> {
    void something() {}
};

大!它是否与纯虚函数的示例开始看起来完全相同?让我们做另一个有效的转变:

template<typename T>
struct IFoo {
    void stuff(T f);
};

struct Foo : IFoo<Foo> {
    void something() {}
};

// Later...
template<typename T>
void IFoo<T>::stuff(T f) {
    f.something();
}

完成!我们在Foo完成后稍后定义了该函数。这很简单:编译器只有在使用时才会实现IFoo<Foo>::stuff。而使用它的地方Foo已经完成了。也没有魔法。

为什么不能在T内声明IFoo成员变量呢?

简单,原因与此代码无法编译的原因相同:

struct Bar;
Bar myBar;

声明一个不完整类型的变量是没有意义的。