2

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

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

如果可能,您将如何为此设计一个满足以下要求的界面?

  • 应该是完全可选的。即应该可以自动确定给定类型是否提供这样的字段,然后容器将仅在可用时使用它。
  • 理想情况下,不依赖于类型特征等,因为我需要最大的编译器兼容性。
  • 应该很容易使用。即,如果您可以并且想要为 type 启用此优化MyStuff,则可以使用 3 行代码而不是 25 行代码来实现。另一方面,内部复杂性无关紧要。
  • 最好应完全排除误报。我的意思是:如果您检查字段foo_bar,则存在这种字段的可能性很小,因为完全不相关的原因(我认为鸭子类型根本不适用于 C++)。更好的方法是检查类型是否ProvidesExternalUseField从我的库中继承标记类,因为这不是偶然的。

编辑

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

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

在这里,大小MyStuff将是 12 个字节,而不是 8 个。

4

1 回答 1

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;

您还可以通过使用指向成员的指针作为模板参数,进一步自动化(模板化)标记界面中的 getter 和 setter。

于 2011-01-26T11:00:02.940 回答