这是基本的CRTP代码。
运行此代码时,输出为“世界”而不是“ hello”。这是为什么?
#include <iostream>
template<typename Derived>
struct A {
void foo()
{
static_cast<Derived*>(this)->bar();
}
};
struct B : A<B>{
void bar()
{
std::cout << "hello" << std::endl;
}
};
struct C : A<C> {
void bar()
{
std::cout << "world" << std::endl;
}
};
int main() {
void* b = new B();
auto c = static_cast<A<C>*>(b);
c->foo();
return 0;
}
我正在使用gcc 5.4
。
主要问题是A<B>
和A<C>
是不同的类型。我想将它们存储在向量中,并像使用虚拟函数的多态继承类型一样使用它。我试图查看将发生void*
存储的情况,但我不知道发生了什么。
答案 0 :(得分:3)
void* b = new B(); auto c = static_cast<A<C>*>(b); c->foo();
这将产生未定义的行为。您正在通过指向不相关类型B
的指针来访问类型A<C>
的对象。这违反了严格的别名。
如果要在向量中存储指向不同对象类型的指针,则应使用常规的旧virtual
函数和继承。 CRTP用于采用通用功能并将其应用于不同类型。 not 不能将两种不同的类型视为相同。
答案 1 :(得分:2)
您不需要CRTP即可查看示例中的内容。让我们简化一下:
#include <iostream>
struct B {
void bar()
{
std::cout << "hello" << std::endl;
}
};
struct C {
void bar()
{
std::cout << "world" << std::endl;
}
};
int main() {
void* b = new B();
auto c = static_cast<C*>(b);
c->bar();
return 0;
}
这将打印world
。我们只是在C
实例上调用了B
方法。这怎么可能呢?使用void*
可以绕过某些规则,但是这些规则仍然存在。您不应将实例视为不是实例。如果这样做,您将获得不确定的行为。
答案 2 :(得分:2)
主要问题是
A<B>
和A<C>
是不同的类型。我想将它们存储在向量中,并像使用虚拟函数的多态继承类型一样使用它。
您可以通过定义可以作为类模板A
的基类的常规类来实现。
struct Base
{
virtual ~Base{} {}
virtual void foo() = 0;
};
template<typename Derived>
struct A : Base {
void foo() override
{
static_cast<Derived*>(this)->bar();
}
};
现在您可以使用:
std::vector<Base*> baseObjects;
baseObjects.push_back(new B());
baseObjects.push_back(new C());
baseObjects[0]->foo();
baseObjects[1]->foo();