我正在创建一个基类,它有一些方法,用于派生类.这个基类类似于抽象类,除了(受保护的)方法之外,它定义了必须在派生类中实现的接口(即公共)方法.但它并不打算用作多态基础,而是它的衍生物将用作一些其他函数/仿函数的模板参数,这将调用接口方法.
鉴于上述情况,我可以使用通常的方法来定义抽象类,例如使用纯虚函数,但是存在一个问题:生成的派生类需要具有标准布局.因此,不允许虚拟功能.但是仍然会有很多衍生物,直到稍后才会使用,我想让编译器检查所需的所有方法是否都使用正确的签名实现(例如int Derived :: f(double)而不是int Derived :: f(float)是不允许的).
考虑到标准布局的要求,这样做的好方法是什么?
最佳答案 这是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写入类型特征来检查接口和实现之间的确切函数签名匹配.
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.请注意,派生类确实是标准布局.