C++ 确保从父CRTP类派生的类实现函数
简介: 我希望确保派生类实现父CRTP类中的函数所需的成员函数 详情: 我有一些这样的代码C++ 确保从父CRTP类派生的类实现函数,c++,c++11,crtp,C++,C++11,Crtp,简介: 我希望确保派生类实现父CRTP类中的函数所需的成员函数 详情: 我有一些这样的代码 class Base { public: class Params { public: virtual ~Params() {} }; virtual void myFunc( Params& p ) = 0; }; template< typename T > class CRTP : public Base { publi
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 );
};
但这不起作用,因为在定义CRTP
时,Imp
尚未完全定义。使用了一个static\u assert
,这让我想到在CRTP::myFunc
中使用static\u 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;
}
类基
{
公众:
类参数
{
公众:
虚拟~Params(){}
};
虚空myFunc(参数S&p)=0;
};
模板
CRTP类:公共基
{
公众:
虚拟void myFunc(基本::参数S&p)最终覆盖
{
typename T::Params&typedParams=动态转换(p);
静态_cast(this)->myFuncImp(typedParams);
}
};
Imp类:公共CRTP
{
公众:
类参数:public CRTP::Params
{
公众:
虚拟~Params(){}
int x,y,z;
};
};
int main(int argc,字符**argv)
{
小鬼小鬼;
}
编译失败,因为
Imp
没有提供myFuncImp
,为派生类的成员使用不同的名称(如Rudolfs Bundulis answer中所述)的想法是好的。但是,我会将此设置为受保护的方法,这样用户就不会尝试使用它
此外,在CRTP库中使用Derived::Params
会带来额外的复杂性(因为Derived=Imp
在CRTP
中使用时未完全声明),因此最好始终将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
struct Base//公共用户界面
{
结构参数{virtual~Params(){}};
虚空myFunc(参数&)=0;
};
命名空间详细信息{//阻止客户端直接访问
模板
结构CRTP:Base
{
无法重写虚拟void myFunc(参数S&p)final//
{
静态投影(this)->myFuncImp(p);
}
};
Imp类:公共CRTP
{
结构参数:CRTP::Params{intx,y,z;};
无效myFuncImpDetails(参数*);
protected://受客户端保护
void myFuncImp(基::参数和参数)
{
自动pars=动态投影(&p);
if(PAR)
myFuncImpDetails(PAR);
其他的
抛出std::runtime_错误(“提供给Imp::myFunc()的参数类型无效”);
}
};
}//名称空间详细信息
您可以中断动态多态性并切换到静态多态性:
#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);
}
#包括
#包括
阶级基础
{
公众:
类参数
{
公众:
虚拟~Params(){}
};
虚拟~Base(){}
虚空myFunc(参数S&p)=0;
};
名称空间详细信息{
//静态断言的助手
//如果“void CRTP::myFunc(Base::Params&)[with T=Imp]”是私有的,则可以省略此选项
结构是可调用的实现
{
模板
静态decltype(std::declval().myFunc(std::declval()),std::true_type())
测试(int);
模板
静态std::false\u类型
试验(…);
};
模板
使用is_MyFunc_callable=decltype(is_MyFunc_callable_实现::test(0));
//中断递归的Helper函数
模板
内联void invokeMyFunc(对象和对象、参数和参数){
static_assert(是_MyFunc_callable::value,“派生类缺少'MyFunc'”);
object.myFunc(参数);
}
}//名称空间详细信息
模板
CRTP类:公共基
{
私人:
//做最后的决定!
虚拟void myFunc(Base::Params&p)覆盖final
{
静态断言(std::是::value的base,?T必须从CRTP派生);
typename T::Params&typeParams=dynamic_cast(p);
细节::invokeMyFunc(static_cast(*this),typeParams);
}
};
Imp类:公共CRTP
{
公众:
类参数:public CRTP::Params
{
公众:
int x=1;
int y=2;
int z=3;
};
//没有此功能:
//错误:静态断言失败:派生类缺少“MyFunc”
//错误:“void CRTP::myFunc(Base::Params&)[with T=Imp]”是私有的
#如果0
无效myFunc(参数S&p){
std::cout您不能使用一些SFINAE魔术来确定Imp::Param
是否不同于Base::Param
,以及Imp::myFunc()
将Imp::Param
作为参数?@Walter对于第二种方法,如果存在继承,则会出现误报。对于第一种方法,您可能不需要这样做。旁白:为什么您要从非const
方法静态强制转换为T const*
?在Base中,myFunc
应该是const
和CRTP
,或者您应该在CRTP
实现中调用static\u cast
。@Yakk误报无关紧要,只要有方法Imp::myfunc(Imp::Params&)
,我们被分类了,不是吗?@Walter该方法可能是Base
中的虚拟方法。除非你认为我们检查一个方法指针,这对我来说似乎是个坏主意:错误否定和/或对可能发生的事情的过度限制。将final
添加到CRTP
中的myFunc
中,以避免混淆
#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);
}