如何确保导数 类 实现特定方法,保留标准布局?
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确实是标准布局
我正在制作一个基础 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确实是标准布局