C ++零大小数组,不需要内存空间?

时间:2013-06-06 20:47:14

标签: c++ arrays templates memory-management stdarray

当声明模板化类的成员变量时,有没有办法让它需要零内存,具体取决于某些模板参数的值?

一个例子是定义类似std::array<T,n>的内容,n==0时需要零空间。

例如:

template<int num_optional_args> class C {
    int some_variable;
    std::array<int,num_optional_args> optional_args;
};

有没有办法在num_optional_args==0

时消除optional_args的开销

std::array<T,n>的大部分实施都为一个T元素预留空间,即使n==0也是如此。

还有其他方法可以保留零空间吗? 为什么这不是C ++标准的一部分?

5 个答案:

答案 0 :(得分:4)

您可以专门化您的类型,以便在数字为零时optional_args不存在。如果您需要该对象存在,那么对象可以存在并且可以被引用的唯一方式实际上不占用空间是通过空基类优化。

您可以通过以下方式使用它:

template<int num_optional_args>
class optional_args {
    std::array<int,num_optional_args> args
public:
    // whatever interface you want for the optional args.
    void foo(int n) {
        if (n < num_optional_args)
            args[n];
        throw std::runtime_error("out of range");
    }
};

template<>
class optional_args<0> {
public:
    // whatever interface you want for the optional args, specialized for 0 args.
    void foo(int n) {
        throw std::runtime_error("out of range");
    }
};

template<int num_optional_args>
class C : optional_args<num_optional_args> {
    int some_variable;
    void bar() {
        for (int i=0; i<num_optional_args; ++i) {
            optional_args::foo(i);
        }
    }
};

答案 1 :(得分:1)

您需要为至少一个元素保留空间,或者保留指向该元素的指针。 不可能有一个采用内存的数组结构。

以下结构在创建时只接受一个int和一个指针,它与您将得到的接近于零:

template<typename T>
class array {

  int sz;
  T *head;

};

除此之外,在类定义中要求零空间的概念是愚蠢的。希望它在实例化时占用接近零的空间可能有意义,可以通过参数化构造函数来完成,如下所示:

template<typename T>
class array {

  int sz;
  T *head;

  array(int n) {
      if (n == 0) return;
      head = new T[n];
  }
};

答案 2 :(得分:1)

正如Praetorian所说,你可以专注于0.如果你想让C类的所有变体具有相同的接口,你可以让所有的C都来自C&lt; 0&gt;,就像这样:     模板类C;

template <> class C<0> {                                                                                                                                                                                                                     

    int s;                                                                                                                                                                                                                                   

public:                                                                                                                                                                                                                                      
    int blah();                                                                                                                                                                                                                              
};                                                                                                                                                                                                                                           

template <int N> class C : public C<0>{                                                                                                                                                                                                      

    int a[N];
};

int C<0>::blah() {return s;}

int main() {

    C<1> a;
    C<0> b;
    a.blah();
    b.blah();
    return 0;
}

答案 3 :(得分:0)

如果您不关心事物的POD,可以使用Boost.CompressedPair:

template<int num_optional_args> class C {
  boost::compressed_pair<int, std::array<int,num_optional_args>> data;
  int& some_variable() { return data.first(); }
  std::array<int,num_optional_args>& optional_args() { return data.second(); }
};

如果std :: array很可能是一个空类,那么应该删除开销。但是你的类不再是POD,因为compressed_pa​​ir不是。

答案 4 :(得分:-1)

我真的不记得它在C ++中是否完全合法,但我认为它仍然在C:你可以有一个零大小的数组,但它必须是结构定义的最后一个成员。从历史上看,它被用于即可变长度缓冲区:

struct buffer
{
    usigned int size;
    byte data[0];
};

buf.data是完全可用的array'n'pointer,所以如果你注意并malloc N + sizeof(int)字节,那么你可以把它转换为buffer,将size设置为N,在这里,您有一个带有data前缀的N字节size数组。关键是每个这样的缓冲区总是以'size'前缀开头,然后有数据,所以你可以将每个这样的缓冲区转换为buffer并检查大小然后使用*(data + x)或者data [x]提供0

例如参见http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

然而,请注意C,而不是C ++,但我几乎可以肯定我在C ++中看到了POD中的这些原始技巧。

除此之外,最重要的一点是这样的数组将具有零长度。在上面的示例中,sizeof(buffer)== sizeof(int),但仅当数组是最后一个成员时。如果你在数组之后添加另一个字段,那么数组和最后一个字段需要有不同的偏移量,这样零-len数组最终会成为1字节(+ align)只是为了有不同的地址。没有提到没有理智的编译器会允许你在结构的中间声明零len数组。它实际上只能作为结构的尾部。