如何确保派生类实现特定方法,保持标准布局?

Posted

技术标签:

【中文标题】如何确保派生类实现特定方法,保持标准布局?【英文标题】:How to ensure that derivative classes implement particular methods, retaining standard layout? 【发布时间】:2015-09-01 17:11:44 【问题描述】:

我正在创建一个基类,它有一些在派生类中使用的方法。这个基类有点像抽象类,除了(protected)方法之外,它还定义了必须在派生类中实现的接口(即public)方法。但它并不打算用作多态基础,而是它的派生类将用作其他一些函数/仿函数的模板参数,这些函数/函子将调用接口方法。

鉴于上述情况,我可以使用通常的方法来定义抽象类,例如使用纯虚函数,但是这样做有一个问题:生成的派生类需要具有标准布局。因此不允许使用虚函数。但是仍然会有很多衍生品,直到稍后才会使用,我想让编译器检查所​​有需要的方法是否使用正确的签名实现(例如int Derived::f(double)而不是int Derived::f(float)不允许)。

考虑到标准布局的要求,这样做的好方法是什么?

【问题讨论】:

也许奇怪重复的模板模式有帮助。所以你的基类调用派生类方法,但是解析是在编译时完成的。 听起来像是概念。见boost.org/doc/libs/1_58_0/libs/concept_check/concept_check.htm @TobySpeight 你为什么不使用“答案”框来写你的答案但使用评论? (@DieterLücking) @Alex - 因为这是推测而不是完整的答案。我没有时间亲自尝试。 @TobySpeight 基类如何检查该方法不仅兼容,而且完全符合要求? (请参阅问题中的示例:int Derived::f(double) 而不是 int Derived::f(float) 是不允许的)。 【参考方案1】:

这是 CRTP 模式的实现,在接口调度例程中带有 static_assert

#include <iostream>
#include <type_traits>

template<class Derived>
class enable_down_cast

        typedef enable_down_cast Base;
public:
        // casting "down" the inheritance hierarchy
        Derived const* self() const  return static_cast<Derived const*>(this); 
        Derived*       self()        return static_cast<Derived*      >(this); 
protected:
        // disable deletion of Derived* through Base*
        // enable deletion of Base* through Derived*
        ~enable_down_cast() = default; 
;

template<class FX>
class FooInterface
:
    public enable_down_cast< FX >

    using enable_down_cast< FX >::self; // dependent name now in scope
public:
    int foo(double d) 
    
        static_assert(std::is_same<decltype(self()->do_foo(d)), int>::value, "");
        return self()->do_foo(d); 
    
protected:
    // disable deletion of Derived* through Base*
    // enable deletion of Base* through Derived*
    ~FooInterface() = default;
;

请注意,上面的static_assert 仅在接口的返回类型和实现不匹配时才会触发。但是您可以使用任何您希望的类型特征来装饰此代码,并且这里有 plenty of Q&As 在 SO 上编写类型特征以检查接口和实现之间的精确函数签名匹配。

class GoodFooImpl
:
    public FooInterface< GoodFooImpl > 

private:
    friend class FooInterface< GoodFooImpl > ;
    int do_foo(double)  std::cout << "GoodFooImpl\n"; return 0; 
;

class BadFooImpl
:
    public FooInterface< BadFooImpl > 

private:
    friend class FooInterface< BadFooImpl >;
    char do_foo(double)  std::cout << "BadFooImpl\n"; return 0; 
;

int main()

    GoodFooImpl f1;         
    BadFooImpl f2;

    static_assert(std::is_standard_layout<GoodFooImpl>::value, "");
    static_assert(std::is_standard_layout<BadFooImpl>::value, "");

    f1.foo(0.0);
    f2.foo(0.0); // ERROR, static_assert fails, char != int

Live Example on Coliru.注意派生类确实是标准布局。

【讨论】:

以上是关于如何确保派生类实现特定方法,保持标准布局?的主要内容,如果未能解决你的问题,请参考以下文章

如何使类工厂创建所需的派生类

pdb可以打印派生类从哪个基类继承特定方法[重复]

C# 接口(Interface)

C++ - 让派生类从基类“继承”重载赋值运算符的安全/标准方法

如何通过指针识别特定的派生类?

抽象派生类中的抽象方法覆盖/实现抽象基类的抽象或具体方法。如何以及为啥?