不需要内存空间的c++零大小数组

C++ zero-size array that requires no memory space?

本文关键字:小数 数组 c++ 内存 空间 不需要      更新时间:2023-10-16

当声明模板化类的成员变量时,是否有一种方法可以根据某些模板参数的值使其需要零内存?

一个例子是定义像std::array<T,n>这样的东西,当n==0

例如:

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

是否有办法消除开销的optional_args当num_optional_args==0 ?

大多数std::array<T,n>实现为一个T元素保留空间,即使n==0 .

是否有另一种方法可以保留零空间?为什么这不是c++标准的一部分?

您可以专门化您的类型,以便当数字为0时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);
        }
    }
};

您要么需要为至少一个元素保留空间,要么保留指向该元素的指针。不可能存在占用 0 内存的数组结构。

下面的结构体在创建时只接受一个int和一个指针,这几乎是接近于0的:

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];
  }
};

正如禁卫军所说,你可以专精0。如果你想让类C的所有变体都有相同的接口,你可以让所有的C都从C<0>派生,像这样:模板类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;
}

如果您不关心您的东西的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_pair不是。

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

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

buf.data是完全可用的数组指针,所以如果你注意malloc N+sizeof(int)字节,那么你可以将其强制转换为buffer,将size设置为N,然后你就得到了一个带有size前缀的N字节data数组。关键是,每个这样的缓冲区将始终具有并以'size'前缀开始,然后具有数据,因此您可以将每个这样的缓冲区强制转换为buffer并检查大小,然后使用*(data+x)或data[x]提供0

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

然而,注意这是C,而不是c++,但我几乎肯定我在c++中也看到过这样的raw-mem技巧。

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