如何解决从 CRTP 基类对象调用派生类方法
有没有一种方法可以从基类对象调用派生类的方法,而基类对象不提供调用接口?我想做这样的事情
template<typename T>
struct A
{
using Derived = T;
void print()
{
static_cast<T*>(this)->print_();
}
void print_()
{
std::cout << "Base" << std::endl;
}
void a()
{
std::cout << "Aa" << std::endl;
}
};
struct B : public A<B>
{
void print_()
{
std::cout << "BD" << std::endl;
}
void b()
{
std::cout << "Bb" << std::endl;
}
};
struct C : public A<C>{
void c()
{
std::cout << "Cc" << std::endl;
}
};
int main()
{
A<B> b;
A<C> c;
b.print();
c.print();
B bd;
b.a();
bd.b(); // This works
// b.b(); // I understand this doesn't work,but I want to make this work.
C bc;
c.a();
bc.c(); // Same as class A<C> and C
}
我可以提供调用这些函数的接口,但我想知道这是否可以实现。非常感谢任何有关可行性的解决方案或评论。
PS:我只是对此很好奇,因为当我错误地实例化类时出现了一个无意的错误。
解决方法
有没有一种方法可以从基类对象调用派生类的方法,而基类不提供调用接口?
是的,正如 Jarod42 所建议的,您可以在 operator->
中实现 A
。您不能重载 operator.
,因此在调用此类方法时必须使用 ->
。
template<typename T>
struct A {
T* operator->() { return static_cast<T*>(this); }
};
你现在可以编译这个:
A<B> b;
b->print_();
b->b();
但是:您的程序将具有未定义的行为。 b
不是 B
。它是一个不从 A<B>
继承的 B
,因此您将在不存在的对象上调用非 static
成员函数。
我建议您避免实例化没有正确 CRTP 关系的 A
:s。
template<typename T>
struct A {
T* operator->() { return static_cast<T*>(this); }
private:
A() = default; // hidden from all ...
friend T; // ... except T
};
您现在可以实例化 B
,但如果有人像这样进行虚假继承,则不能实例化 A<B>
或 C
:
struct X {};
struct C : A<X> {}; // C can't be instantiated. A is friend of X,not C
,
简单案例:如果您添加静态函数,则有一种安全的方法(因为它不会访问实例日期)。
复杂的情况:b.b():这个几乎有效,但绝对是个坏主意。只要函数不引用任何实例变量,它就会相对安全。但否则它肯定会崩溃。原因是 b
没有 B
实例。
template<typename T>
struct A
{
typedef typename T Derived;
typedef typename A<T> AT;
void print()
{
static_cast<T*>(this)->print_();
}
void print_()
{
std::cout << "Base" << std::endl;
}
void a()
{
std::cout << "Aa" << std::endl;
}
//these conversion operators are for b.b() case. very bad idea!
operator Derived* () {
return static_cast<T*>(this);
}
operator Derived& () {
return static_cast<Derived&>(*this);
}
};
struct B : public A<B>
{
void print_()
{
std::cout << "BD" << std::endl;
}
static void static_b()
{
std::cout << "Bb::static_b" << std::endl;
}
void b()
{
std::cout << "Bb::b" << std::endl;
}
};
void test()
{
b.A<B>::Derived::static_b(); // This should work.
b.AT::Derived::static_b(); // This works with "AT" but it's recursive template. Not good.
((B&)b).b(); //This works but even though operator is implicit,it cannot implicitly know what to do. Nor will "auto".
//This works but even though operator is implicit,it cannot implicitly know what to do. Nor will "auto".
B& br = b;
br.b();
}
谢谢, -尼尔。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。