代码之家  ›  专栏  ›  技术社区  ›  doublep

将字段指定为“外部使用”的任意类类型

  •  2
  • doublep  · 技术社区  · 16 年前

    我的容器需要存储一些关于其元素的信息。通常,我会将其与元素分开存储。然而,我想通过在元素结构类型中指定一个字段供外部使用,为用户提供节省内存的可能性。例如。:

    struct MyStuff
    {
      int           foo;
      char          bar;
      mutable char  dedicated_for_external_use;  // Because of alignment, this field
                                                 // won't increase sizeof (MyStuff)
    };
    

    这里的想法是,除了元素的容器之外,任何东西都不能访问字段。因为容器存储副本(很像 std::vector ),如果您添加了任何给定的值,这不会是一个问题 x 放在几个容器里。

    您将如何为此设计一个接口,如果可能的话,它将满足以下要求?

    • 应该是完全可选的。也就是说,应该可以 自动地 确定给定类型是否提供这样的字段,然后容器将仅在可用时使用它。
    • 理想情况下,它不会依赖于类型特征等,因为我需要最大的编译器兼容性。
    • 应该易于使用。也就是说,如果您能够并且希望对类型进行此优化 MyStuff ,你可以用3行代码,而不是25行。另一方面,内部并发症并不重要。
    • 最好完全排除假阳性。我的意思是:如果你检查字段 foo_bar 有一个很小的可能性,这类字段的存在是因为一个完全无关的原因(我认为duck类型根本不适合C++)。更好的方法是检查类型是否继承marker类 ProvidesExternalUseField 从我的图书馆,因为这不可能是偶然的。

    编辑

    我知道Boost的事。打扰,但我想要的是不同的东西。如果我这样做,用一个 char 字段,它在许多情况下不能用于保存内存。如果继承的类型有 int 作为第一个领域, 烧焦 字段将填充为4字节。也就是说,您通常需要复杂的类型内部知识才能“压缩”这样的外部使用字段,但继承并不能真正提供它:

    struct hooks { mutable char dedicated_for_external_use; };
    struct MyStuff : hooks
    {
      int           foo;
      char          bar;
    };
    

    这里是 迈斯塔夫 将是12字节,而不是8字节。

    1 回复  |  直到 16 年前
        1
  •  0
  •   dnlgl    15 年前

    对于数据结构从标记接口派生的情况,可以使用部分模板专门化。

    假设您的marker接口类如下所示:

    class ProvidesExternalUseField
    {
    public:
        char GetExtraField () { return 0; }
        void SetExtraField (char newVal) {}
    };
    

    它不是虚拟的:我们不想仅仅为了这个目的而添加一个指向数据类的vtable指针。

    现在让我们实现一个简单的容器类:

    template <class T>
    class Container
    {
    public:
        char GetExtraValue ()
        {
            return 0; // here we cannot know if T is derived from the marker
        }
    private:
        T m_t;
    };
    

    下面是我们如何改变它来区分这两种情况:

    template <class T, bool DoesTProvideExternalUseField>
    class ContainerImpl
    {
    public:
        char GetExtraValue () { return 0; }
    
    private:
        T m_t;
    };
    
    template <class T>
    class ContainerImpl<T, true>
    {
    public:
        char GetExtraValue () { return m_t.GetExtraField(); } 
    private:
        T m_t;
    };
    
    template <class T>
    class Container: public ContainerImpl<T,
                                          boost::is_base_of<ProvidesExternalUseField,T>::value>
    {
    };
    

    现在您可以这样定义结构:

    struct A
    {
        int m_intVal;
    };
    
    struct B: public ProvidesExternalUseField
    {
        char GetExtraField () { return m_extraField; }
        void SetExtraField (char newVal) { m_extraField = newVal; }
    
        int m_intVal;
        char m_charVal;
        char m_extraField;
    };
    

    并以完全相同的方式使用容器类:

    Container<A> a;
    Container<B> b;
    

    还可以通过使用poiter to member作为模板参数,进一步自动化(模板化)标记接口中的getter和setter。