微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

将抽象类实现为其他类的接口,而无需 vtable 开销

如何解决将抽象类实现为其他类的接口,而无需 vtable 开销

我想制作接口类ForwardIteratorBiderctionalIteratorRandomAccessIterator,每一个都有一些操作符(都是纯虚的,没有实现)。现在如果我想为一个容器实现一个迭代器,如果我不小心忘记实现一些函数/操作符,我只需从正确的迭代器继承并获得编译器的帮助。 用纯虚函数做这件事很完美,但它有 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)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。