如何确保导数 类 实现特定方法,保留标准布局?

How to ensure that derivative classes implement particular methods, retaining standard layout?

我正在制作一个基础 class,它有一些方法,用于派生的 classes。这个基础 class 在某种意义上类似于抽象 class,除了 (protected) 方法之外,它还定义了必须在其中实现的接口(即 public)方法派生 classes。但它并不打算用作多态基础,而是它的衍生物将用作其他一些 functions/functors 的模板参数,这将调用接口方法。

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

考虑到标准布局的要求,执行此操作的好方法是什么?

这是在接口调度例程中使用 static_assert 的 CRTP 模式的实现:

#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 仅在接口的 return 类型与实现不匹配时才会触发。但是你可以用你想要的任何类型特征来修饰这段代码,SO 上有 plenty of Q&As 写类型特征来检查接口和实现之间的函数签名是否完全匹配。

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.注意导出的class确实是标准布局