确保从父CRTP类派生的类实现函数
Ensure that class derived from parent CRTP class implements function
简介:
我想确保派生类实现父CRTP类中函数所需的成员函数。
详细信息:
我有一些类似的代码
class Base
{
public:
class Params
{
public:
virtual ~Params() {}
};
virtual void myFunc( Params& p ) = 0;
};
template< typename T >
class CRTP : public Base
{
public:
virtual void myFunc( Base::Params& p ) override
{
typename T::Params& typedParams = dynamic_cast<typename T::Params&>( p );
static_cast<T*>( this )->myFunc( typeParams );
}
};
class Imp : public CRTP<Imp>
{
public:
class Params : public CRTP<Imp>::Params
{
public:
virtual ~Params() {}
int x, y, z;
};
virtual void myFunc( Imp::Params& p );
};
目的是我可以有多个Imp
子类,它们都在myFunc
中做不同的事情,并接受它们自己所需的参数。Base
提供的接口然后被更高级别的函数使用,这些函数只需要具有类型为Base::Params
和Base
的指针/引用。我的问题是确保任何Imp
都提供一个专门的myFunc
。为了避免无限递归,Imp
必须实现myFunc
。
我的第一次尝试是在CRTP
中添加一个纯虚拟函数
virtual void myFunc( typename T::Params& p ) = 0;
但这不起作用,因为在定义CCD_ 12时CCD_。这个问题使用了一个static_assert
,这让我想到了在CRTP::myFunc
中使用static_assert
。只是我不确定非静态函数的静态断言中的表达式应该是什么。
- 我可以用
static_assert
来满足我的需求吗 - 这是确保派生类具有所需功能的最佳/最干净的方法吗
- 我是不是被我的课堂设计迷住了,还有更好的做事方式
谢谢。
为什么不为函数使用不同的名称?然后,对于没有和实现的CRTP
类的每个派生,都会出现编译错误。考虑一下:
class Base
{
public:
class Params
{
public:
virtual ~Params() {}
};
virtual void myFunc( Params& p ) = 0;
};
template< typename T >
class CRTP : public Base
{
public:
virtual void myFunc( Base::Params& p ) final override
{
typename T::Params& typedParams = dynamic_cast<typename T::Params&>( p );
static_cast<const T*>( this )->myFuncImp( typedParams );
}
};
class Imp : public CRTP<Imp>
{
public:
class Params : public CRTP<Imp>::Params
{
public:
virtual ~Params() {}
int x, y, z;
};
};
int main(int argc, char** argv)
{
Imp imp;
}
由于Imp
没有提供myFuncImp
,编译失败。
您可以打破动态多态性并切换到静态多态性:
#include <iostream>
#include <type_traits>
class Base
{
public:
class Params
{
public:
virtual ~Params() {}
};
virtual ~Base() {}
virtual void myFunc(Params& p) = 0;
};
namespace Detail {
// Helper for the static assertion
// Omit this if "‘void CRTP<T>::myFunc(Base::Params&) [with T = Imp]’ is private" is good enough
struct is_MyFunc_callable_implementation
{
template<typename Object, typename Params>
static decltype(std::declval<Object>().myFunc(std::declval<Params&>()), std::true_type())
test(int);
template<typename Object, typename Params>
static std::false_type
test(...);
};
template<typename Object, typename... A>
using is_MyFunc_callable = decltype(is_MyFunc_callable_implementation::test<Object, A...>(0));
// Helper function to break recursion
template<typename Object, typename Params>
inline void invokeMyFunc(Object& object, Params& params) {
static_assert(is_MyFunc_callable<Object, Params>::value, "The derived class is missing 'MyFunc'");
object.myFunc(params);
}
} // namespace Detail
template<typename T>
class CRTP: public Base
{
private:
// Make this final!
virtual void myFunc(Base::Params& p) override final
{
static_assert(std::is_base_of<Base, T>::value, "T must derive from CRTP");
typename T::Params& typeParams = dynamic_cast<typename T::Params&>(p);
Detail::invokeMyFunc(static_cast<T&>(*this), typeParams);
}
};
class Imp: public CRTP<Imp>
{
public:
class Params: public CRTP<Imp>::Params
{
public:
int x = 1;
int y = 2;
int z = 3;
};
// Without this function:
// error: static assertion failed: The derived class is missing 'MyFunc'
// error: ‘void CRTP<T>::myFunc(Base::Params&) [with T = Imp]’ is private
#if 0
void myFunc(Params& p) {
std::cout << p.x << p.y << p.z << 'n';
}
#endif
};
int main()
{
Imp imp;
Base* base = &imp;
Imp::Params params;
base->myFunc(params);
}
然而,我的观点是:基类设计是失败的,上面的代码只是一个变通方案。
为派生类的成员使用不同名称的想法(如Rudolfs-Bundulis的回答(很好。然而,我会将其作为protected
方法,这样用户就不会尝试使用它
此外,在CRTP库中使用Derived::Params
会增加额外的复杂性(因为Derived=Imp
在CRTP<Imp>
中使用时没有完全声明(,因此最好始终将Base::Params
作为函数参数。
struct Base // public user interface
{
struct Params { virtual ~Params() {} };
virtual void myFunc( Params& ) = 0;
};
namespace details { // deter clients from direct access
template< typename Derived >
struct CRTP : Base
{
virtual void myFunc( Params& p ) final // cannot be overridden
{
static_cast<Derived*>( this )->myFuncImp(p);
}
};
class Imp : public CRTP<Imp>
{
struct Params : CRTP<Imp>::Params { int x, y, z; };
void myFuncImpDetails( Params* );
protected: // protected from clients
void myFuncImp( Base::Params& p )
{
auto pars=dynamic_cast<Params*>(&p);
if(pars)
myFuncImpDetails(pars);
else
throw std::runtime_error("invalid parameter type provided to Imp::myFunc()");
}
};
} // namespace details
相关文章:
- 在命名空间内部还是外部实现 c++ 函数?
- 如何在C++中实现函数(f)(x)(y, z)(g)(r)
- 如何在C++中实现函数上的二叉搜索?
- 键按下事件错误 Qt 实现函数时
- 在子类之外实现函数导致未知错误
- 通过模板滥用实现函数式C++
- C++,实现函数"int next(std::string param)"时出现奇怪的编译器错误
- 我正在尝试创建自己的 strcat() 函数,而不是使用库实现函数<cstring>
- 如果未实现函数,则在链接时启用错误
- 用函数参数实现c++函数指针
- 如何在Android SDK中调用和实现C++函数
- 在 C++ RCPP 中实现 R 函数
- 实现函数模板填充多维对象
- 类的层次结构,试图在基本级别实现函数
- 用c++实现函数对象的自动检测类型
- 用shared_ptrs实现函数模板的C++实例化
- 在类中实现函数导致错误:成员引用基类型'ifstream (string)'不是结构或联合
- 用c++实现S函数.生成错误
- 引用可以用于实现函数重写吗
- 如何在c++中实现函数超时