问题描述
我想制作接口类ForwardIterator
、BiderctionalIterator
和RandomAccessIterator
,每一个都有一些操作符(都是纯虚的,没有实现)。现在如果我想为一个容器实现一个迭代器,如果我不小心忘记实现一些函数/操作符,我只需从正确的迭代器继承并获得编译器的帮助。
用纯虚函数做这件事很完美,但它有 vtable 的开销,这是不必要的,因为所有代码需要都可以在编译时定义。
template <typename T>
struct ForwardIterator{
virtual T operator++() = 0;
virtual T operator++(int) = 0;
};
template <typename T>
struct BidirectionalIterator: public ForwardIterator<T>{
virtual T operator--() = 0;
virtual T operator--(int) = 0;
};
template <typename T>
struct RandomAccessIterator: public Bidirectional<T>{
virtual T operator+(int) = 0;
virtual T operator-(int) = 0;
};
// Custom Iterator Implementation
class MyCustomrandomAccessIterator
: public RandomAccessIterator<MyCustomrandomAccessIterator>{
// get errors if I miss some function deFinitions.
// but it has vtable !!!
};
解决方法
如果我理解正确,您希望确保在最终派生类中实现了某些方法,但没有使用经典方式(抽象继承),因为它太重了。
我的第一个想法是简单地声明而不是定义方法
template <typename T>
struct ForwardIterator {
T operator++ ();
T operator++ (int);
};
所以当你使用这些方法时
MyCustomRandomAccessIterator i;
++i;
您收到链接器错误。
但是您会收到链接器错误(我想您更喜欢编译错误)并且仅当您使用被遗忘的方法时,因此如果您忘记了单个方法可能很难检测到它(您需要将它与对象一起使用) .
所以我的想法,也许并不完美,是 delete
方法,添加一个构造函数并检查最终类中方法的存在。
我的意思是......就像
template <typename T>
struct ForwardIterator {
T operator++ () = delete;
T operator++ (int) = delete;
ForwardIterator ()
{
static_assert( sizeof(decltype(std::declval<T>()++)),"!" );
static_assert( sizeof(decltype(++std::declval<T>())),"!" );
}
};
这样你就可以得到所有编译错误(也许,如果你愿意,带有有意义的错误消息,比 "!"
更好)只需声明你的类的对象
MyCustomRandomAccessIterator i;
// ++i; // no needs of invoke the forgotten methods to get the errors
下面是一个完整的编译示例
#include <utility>
template <typename T>
struct ForwardIterator {
T operator++ () = delete;
T operator++ (int) = delete;
ForwardIterator ()
{
static_assert( sizeof(decltype(std::declval<T>()++)),"!" );
}
};
template <typename T>
struct BidirectionalIterator: public ForwardIterator<T> {
T operator-- () = delete;
T operator-- (int) = delete;
BidirectionalIterator ()
{
static_assert( sizeof(decltype(std::declval<T>()--)),"!" );
static_assert( sizeof(decltype(--std::declval<T>())),"!" );
}
};
template <typename T>
struct RandomAccessIterator: public BidirectionalIterator<T>{
T operator+ (int) = delete;
T operator- (int) = delete;
RandomAccessIterator ()
{
static_assert( sizeof(decltype(std::declval<T>()+0)),"!" );
static_assert( sizeof(decltype(std::declval<T>()-0)),"!" );
}
};
// Custom Iterator Implementation
struct MyCustomRandomAccessIterator
: public RandomAccessIterator<MyCustomRandomAccessIterator> {
// with `if 0` (forgetting definition of requested methods) you get
// a lot of compilation errors
#if 1
MyCustomRandomAccessIterator operator++ ()
{ return *this; }
MyCustomRandomAccessIterator operator++ (int)
{ return *this; }
MyCustomRandomAccessIterator operator-- ()
{ return *this; }
MyCustomRandomAccessIterator operator-- (int)
{ return *this; }
MyCustomRandomAccessIterator operator+ (int)
{ return *this; }
MyCustomRandomAccessIterator operator- (int)
{ return *this; }
#endif
};
int main()
{
// simply declaring i you get a lot of errors,in case of no
// methods definitions
MyCustomRandomAccessIterator i;
}
,
你想要的是奇怪重复出现的模板模式 https://en.m.wikipedia.org/wiki/Curiously_recurring_template_pattern 它允许静态多态性,即您可以将“this”指针转换为子类的指针,您可以将类型作为模板参数传递给子类,并在没有 vtable 的情况下从基类中的子类调用方法。为了区分迭代器的类型,您还可以将与类型相对应的迭代器标记作为模板参数传递给基类。这就是 Boost.STLInterfaces 的工作原理,例如 (https://www.boost.org/doc/libs/1_74_0/doc/html/stl_interfaces.html)。