为“外部使用”专用任意类类型的字段

时间:2010-08-08 12:24:12

标签: c++ interface-design

我的容器需要存储有关其元素的一些信息。通常,我将它与元素分开存储。但是,我想通过在元素结构类型中专用一个字段供外部使用,为用户提供节省内存的可能性。 E.g:

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,那么存在一个很小的可能性,这个字段存在是出于完全不相关的原因(我认为鸭子输入根本不适用于C ++)。更好的方法是检查类型是否从我的库继承标记类ProvidesExternalUseField,因为这不是偶然的。

修改

我知道Boost.Intrusive,但我想要的是不同的东西。如果我这样做并使用单个char字段创建一个钩子类,在许多情况下它不能用于节省内存。如果继承类型的第一个字段为int,则char字段将填充为4个字节。即你经常需要复杂的类型内部知识才能“挤压”这样的外部使用字段,但继承并没有真正提供它:

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

此处,MyStuff的大小为12个字节,而不是8个。

1 个答案:

答案 0 :(得分:0)

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

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

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。