C++ 确保从父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

简介:

我希望确保派生类实现父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 );
};
但这不起作用,因为在定义
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);
    }