使用CRTP在静态多态性中模拟纯虚拟函数是可能的
Is emulating pure virtual function in static polymorphism using CRTP possible?
我正在尝试使用CRTP实现编译时多态性,并希望强制派生类实现该函数。
当前的实现是这样的。
template <class Derived>
struct base {
void f() {
static_cast<Derived*>(this)->f();
}
};
struct derived : base<derived>
{
void f() {
...
}
};
在这个实现中,如果派生类没有实现f()
,那么对函数的调用将陷入无限循环。
如何强制派生类实现类似于纯虚拟函数的函数?我尝试像static_assert(&base::f != &Derived::f, "...")
一样使用"static_assert",但它会生成一条错误消息,指出指向不同类的成员函数的两个成员函数指针不可比较。
您可以给覆盖的东西和钩子不同的名称,如下所示:
template <class Derived>
struct base {
void f() {
static_cast<Derived*>(this)->fimpl();
}
void fimpl() = delete;
};
struct derived : base<derived> {
void fimpl() { printf("hello worldn"); }
};
这里,fimpl = delete
在基中,这样它就不会被意外调用,除非fimpl
在派生类中被重写。
您也可以在CRTP中粘贴一个中间隐藏层,以"临时"将f
标记为delete
:
template <class Derived>
struct base {
void f() {
static_cast<Derived*>(this)->f();
}
};
template <class Derived>
struct intermediate : base<Derived> {
void f() = delete;
};
struct derived : intermediate<derived> {
void f() { printf("hello worldn"); }
};
template<typename Derived>
class Base
{
private:
static void verify(void (Derived::*)()) {}
public:
void f()
{
verify(&Derived::f);
static_cast<Derived*>(this)->f();
}
};
如果派生类不单独实现f
,那么&Derived::f
的类型将是void (Base::*)()
,这将中断编译。
由于C++11,我们也可以使这个函数通用可变模板。
template<typename Derived>
class Base
{
private:
template<typename T, typename...Args>
static void verify(T (Derived::*)(Args...)) {}
};
这是几年前提出的一个问题,但我最近遇到了这个问题,所以我将把它发布在这里,希望它能帮助一些人。
使用auto
作为返回类型可能是另一种解决方案。考虑以下代码:
template<typename Derived>
class Base
{
public:
auto f()
{
static_cast<Derived*>(this)->f();
}
};
如果派生类没有提供有效的重载,则此函数将变为递归函数,并且由于auto
需要最终返回类型,因此永远无法推导它,因此可以保证会引发编译错误。例如,在MSVC上,它类似于:
a function that returns 'auto' cannot be used before it is defined
这迫使派生类提供实现,就像纯虚拟函数一样。
好处是不需要额外的代码,如果派生类也使用auto
作为返回类型,那么这个链可以根据需要运行。在某些情况下,它可以是方便和灵活的,如以下代码中的Base
和LevelTwo
,它们在调用同一接口f
时可以返回不同的类型但是这个链完全禁止从基类直接继承实现,如LevelThree
:
template<typename Derived = void>
class Base
{
public:
Base() = default;
~Base() = default;
// interface
auto f()
{
return fImpl();
}
protected:
// implementation chain
auto fImpl()
{
if constexpr (std::is_same_v<Derived, void>)
{
return int(1);
}
else
{
static_cast<Derived*>(this)->fImpl();
}
}
};
template<typename Derived = void>
class LevelTwo : public Base<LevelTwo>
{
public:
LevelTwo() = default;
~LevelTwo() = default;
// inherit interface
using Base<LevelTwo>::f;
protected:
// provide overload
auto fImpl()
{
if constexpr (std::is_same_v<Derived, void>)
{
return float(2);
}
else
{
static_cast<Derived*>(this)->fImpl();
}
}
friend Base;
};
template<typename Derived = void>
class LevelThree : public LevelTwo<LevelThree>
{
public:
LevelThree() = default;
~LevelThree() = default;
using LevelTwo<LevelThree>::f;
protected:
// doesn't provide new implementation, compilation error here
using LevelTwo<LevelThree>::fImpl;
friend LevelTwo;
};
在我的例子中,我处理的派生类也派生自另一个类,该类提供了确定是停止在当前类还是转到派生类所需的额外信息。但在其他情况下,要么使用实际类型而不是"auto"来打破链条,要么使用其他一些技巧。但在这种情况下,虚拟函数可能是的最佳选择。
- C++无法定义虚拟函数 OUTER 类和头文件
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 尝试将unique_ptrs推送到向量时使用纯虚拟函数错误
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- 当覆盖存在时调用基本虚拟"binded to object"函数
- 如何在C++中伪造虚拟可变参数函数模板?
- 类型擦除的std::function与虚拟函数调用的开销
- 重写虚拟函数和继承
- 是否可以使用函数指针调用虚拟析构函数?
- 在没有动态内存的世界中,我是否需要虚拟析构函数?
- 虚拟继承基构造函数消除
- "虚拟""覆盖"析构函数
- 类中的虚拟布尔函数参数不起作用
- 用纯虚拟函数兜圈子
- 将C++子类成员函数(虚拟实现)传递给 C 类型函数指针
- 尝试在 QLabel 上绘画失败(无法在没有对象的情况下调用成员函数"虚拟无效 QLabel::p aintEvent(QPaintEvent*)")
- 声明析构函数虚拟就足够了吗?
- 视觉 C++当我们在基类中使函数成为纯虚拟时,那么在子类中再次使相同的函数虚拟的必要性是什么
- 重载函数(虚拟/非虚拟)