C++ 使用继承类从模板类继承
当我从类继承时,编译器必须知道基类的定义才能创建它。但是,当我使用自己的继承类从模板类继承时,编译器如何创建代码?它还不知道班级的规模C++ 使用继承类从模板类继承,c++,templates,inheritance,interface,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 : publi
#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未定义,正如我所预料的那样。对于模板类IFoo的定义,编译器不需要知道Foo的大小来布局IFoo
Foo在这个上下文中是一个不完整的类,不是未定义或未声明的,并且可以以任何不完整类型都可以使用的方式使用。出现在成员函数参数列表中是可以的。将成员变量声明为Foo*很好。禁止将成员变量声明为Foo,这是必需的完整类型。使用此模板类IFoo的定义,编译器无需知道Foo的大小即可布局IFoo
Foo在这个上下文中是一个不完整的类,不是未定义或未声明的,并且可以以任何不完整类型都可以使用的方式使用。出现在成员函数参数列表中是可以的。将成员变量声明为Foo*很好。将成员变量声明为Foo是不允许的,因为需要完整类型。这里的一般概念称为奇怪的重复模板模式或CRTP。在上面搜索会得到很多点击率。请参阅: 不过,有一个简单的解释可能会回答您的问题,而不会过多地涉及CRTP。在C和C++中允许以下情况:
struct foo {
struct foo *next;
...
};
或具有两种类型:
struct foo;
struct bar;
struct foo {
struct bar *first;
...
};
struct bar {
struct foo *second;
...
};
只要只使用指向结构或类的指针,类型的完整定义就不必可用。可以通过多种方式将模板分层,并且必须清楚地分别说明模板参数化的类型及其在模板中的使用。添加SFINAE替换失败并不是一个错误,人们甚至可以使没有被实例化的模板得到实例化,因为事情不能用给定的类型来完成。这里的一般概念称为奇怪的重复模板模式或CRTP。在上面搜索会得到很多点击率。请参阅: 不过,有一个简单的解释可能会回答您的问题,而不会过多地涉及CRTP。在C和C++中允许以下情况:
struct foo {
struct foo *next;
...
};
或具有两种类型:
struct foo;
struct bar;
struct foo {
struct bar *first;
...
};
struct bar {
struct foo *second;
...
};
只要只使用指向结构或类的指针,类型的完整定义就不必可用。可以通过多种方式将模板分层,并且必须清楚地分别说明模板参数化的类型及其在模板中的使用。在SFINAE中添加替换失败并不是一个错误,人们甚至可以使没有替换的模板得到实例化,因为在给定类型下无法完成任务
编译器如何创建代码
回答这个问题和回答这个问题是一样的:编译器如何编译它
如何定义一个不存在的类型并声明一个使用该类型的函数
答案很简单:没有真正使用不存在的类型进行编译的代码。既然没有代码可编译,它怎么会失败呢
现在,您可能想知道is与您的代码有什么关系?它如何使一个类能够将自己作为模板参数发送给它的父类
让我们分析编译器在执行此操作时看到的内容:
struct Foo : IFoo<Foo> { /* ... */ };
编译器现在知道Foo存在,但它是一个不完整的类型
现在,他看到:
... : IFoo<Foo> ...
实际上,它声明了一个类,其中声明了一个函数。您在上面看到,使用不完整类型声明函数是有效的。同样的情况也发生在这里。此时,您的代码是可能的,因为此代码是:
struct Foo;
template struct IFoo<Foo>; // instanciate IFoo with Foo
太好了!它是否开始看起来与您使用纯虚拟函数的示例完全相同?让我们进行另一个有效转换:
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();
}
声明不完整类型的变量没有意义
编译器如何创建代码
回答这个问题和回答这个问题是一样的:编译器如何编译它
如何定义一个不存在的类型并声明一个使用该类型的函数
答案很简单:没有真正使用不存在的类型进行编译的代码。既然没有代码可编译,它怎么会失败呢
现在,您可能想知道is与您的代码有什么关系?它如何使一个类能够将自己作为模板参数发送给它的父类
让我们分析编译器在执行此操作时看到的内容:
struct Foo : IFoo<Foo> { /* ... */ };
编译器现在知道Foo存在,但它是一个不完整的类型
现在,他看到:
... : IFoo<Foo> ...
实际上,它声明了一个类,其中声明了一个函数。你看到ab了吗
声明类型不完整的函数是否有效。同样的情况也发生在这里。此时,您的代码是可能的,因为此代码是:
struct Foo;
template struct IFoo<Foo>; // instanciate IFoo with Foo
太好了!它是否开始看起来与您使用纯虚拟函数的示例完全相同?让我们进行另一个有效转换:
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();
}
声明一个不完整类型的变量是没有意义的。memberVar会使Foo间接地持有它自己的另一个实例。基本上,这将使sizeofFoo==2*sizeofFoo。所以当然不行了-谢谢你所有漂亮的回答。现在我很清楚了,memberVar会让Foo间接地拥有它自己的另一个实例。基本上,这将使sizeofFoo==2*sizeofFoo。所以当然不行了-谢谢你所有漂亮的回答。现在我明白了。