派生模板 class 对象的实例化
Instantiation of derived template class objects
我有 class 模板作为派生 class 的基础 class。这个想法是通过 CRTP 技巧利用 "static polymorphic"。
#include <iostream>
template <typename T>
class BASE
{
public:
void write() {static_cast<T*>(this)->write(); }
};
class DER1 : public BASE<DER1>
{
public:
void write(){
std::cout << "Calling write() inside DER1 " << number << std::endl;}
private:
int number = 11;
};
我尝试了两种不同的方法来实例化派生的 class 对象,我发现这两种方法中的一种是不正确的。但我不明白为什么。
int main(void) {
BASE<DER1> der1_objA ;
der1_objA.write();
DER1 der1_objB ;
der1_objB.write();
return 0;
}
事实上,我得到了输出
Calling write() inside DER1 1880535040 [ random number]
Calling write() inside DER1 11 [correct number ]
任何人都可以向我解释问题出在哪里吗?
非常感谢您。
在 BASE<DER1>
实例中没有 DER1
实例,只有一个 BASE
试图将自己转换成它不是的东西。
PS: this talk 关于c++ 内存模型非常相关,但超出了我可以用简单的语言解释的范围。
当你定义一个 BASE 类型的对象时,它 只是 一个 BASE,但在它里面你将 this 指针指向它不是(DER1)的东西,然后继续通过那个无效的指针使用它。那是未定义的行为,垃圾是正常的结果。 CRTP 工作的唯一时间是对象的动态类型实际上是传递给基础的模板参数 class。也就是说,如果 BASE 认为它真的是 DER1,那么它就真的一定是 DER1。当它只是 BASE 时将其自身转换为 DER1 并将该指针用于 DER1 操作是未定义的行为,与这样做没有太大区别:
int x = 42;
std::string * sptr = (std::string*)&x; // extremely questionable
sptr->clear(); // undefined behavior
您应该考虑使 BASE 构造函数具有 "protected" 访问级别,以防止简单的误用情况。这样基础构造函数只能被派生对象调用,所以你不会不小心在隔离中实例化基础:
template <typename T>
class BASE
{
protected:
BASE() = default;
BASE(BASE const&) = default;
public:
void write() {static_cast<T*>(this)->write(); }
};
然后你得到的不是垃圾,而是:
BASE<DER1> objA ;
objA.write();
error: 'BASE<T>::BASE() [with T = DER1]' is protected within this context
BASE<DER1> base;
我有 class 模板作为派生 class 的基础 class。这个想法是通过 CRTP 技巧利用 "static polymorphic"。
#include <iostream>
template <typename T>
class BASE
{
public:
void write() {static_cast<T*>(this)->write(); }
};
class DER1 : public BASE<DER1>
{
public:
void write(){
std::cout << "Calling write() inside DER1 " << number << std::endl;}
private:
int number = 11;
};
我尝试了两种不同的方法来实例化派生的 class 对象,我发现这两种方法中的一种是不正确的。但我不明白为什么。
int main(void) {
BASE<DER1> der1_objA ;
der1_objA.write();
DER1 der1_objB ;
der1_objB.write();
return 0;
}
事实上,我得到了输出
Calling write() inside DER1 1880535040 [ random number]
Calling write() inside DER1 11 [correct number ]
任何人都可以向我解释问题出在哪里吗? 非常感谢您。
在 BASE<DER1>
实例中没有 DER1
实例,只有一个 BASE
试图将自己转换成它不是的东西。
PS: this talk 关于c++ 内存模型非常相关,但超出了我可以用简单的语言解释的范围。
当你定义一个 BASE 类型的对象时,它 只是 一个 BASE,但在它里面你将 this 指针指向它不是(DER1)的东西,然后继续通过那个无效的指针使用它。那是未定义的行为,垃圾是正常的结果。 CRTP 工作的唯一时间是对象的动态类型实际上是传递给基础的模板参数 class。也就是说,如果 BASE 认为它真的是 DER1,那么它就真的一定是 DER1。当它只是 BASE 时将其自身转换为 DER1 并将该指针用于 DER1 操作是未定义的行为,与这样做没有太大区别:
int x = 42;
std::string * sptr = (std::string*)&x; // extremely questionable
sptr->clear(); // undefined behavior
您应该考虑使 BASE 构造函数具有 "protected" 访问级别,以防止简单的误用情况。这样基础构造函数只能被派生对象调用,所以你不会不小心在隔离中实例化基础:
template <typename T>
class BASE
{
protected:
BASE() = default;
BASE(BASE const&) = default;
public:
void write() {static_cast<T*>(this)->write(); }
};
然后你得到的不是垃圾,而是:
BASE<DER1> objA ;
objA.write();
error: 'BASE<T>::BASE() [with T = DER1]' is protected within this context
BASE<DER1> base;