If I understand correctly, you want to be sure that some methods are implemented in the final derived classes but without using the classic way (abstract inheritance) because it's too heavy.
My first idea was simply declare and not define the methods
template <typename T>
struct ForwardIterator {
T operator++ ();
T operator++ (int);
};
so when you use the methods
MyCustomRandomAccessIterator i;
++i;
you get a linker error.
But you get a linker error (and I suppose you prefer a compilation error) and only when you use a forgotten method, so if you forget a single method can be difficult detect it (you need to use it with an object).
So my idea, maybe not perfect, is delete
the methods, add a constructor and check the existence of the methods in the final class.
I mean... something as
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>())), "!" );
}
};
This way you get all compilation errors (maybe, if you want, with meaningful error messages, better than "!"
) simply declaring an object of your class
MyCustomRandomAccessIterator i;
// ++i; // no needs of invoke the forgotten methods to get the errors
The following is a full compiling example
#include <utility>
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>())), "!" );
}
};
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;
}