3

在声明模板类的成员变量时,有没有办法根据某些模板参数的值使其需要零内存?

一个例子是定义类似的东西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++ 标准的一部分?

4

5 回答 5

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);
        }
    }
};
于 2013-06-06T21:13:07.277 回答
1

正如 Praetorian 所说,您可以专门针对 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;
}
于 2013-06-06T21:13:55.667 回答
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];
  }
};
于 2013-06-06T20:50:39.990 回答
0

如果你不关心你的东西的 POD-ness,你可以使用 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 不是。

于 2013-06-14T14:05:08.330 回答
-1

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

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

是完全可用的数组' buf.datan'指针,所以如果你小心和mallocN+sizeof(int) 字节,那么你可以将它转换为 a buffer,将它设置size为 N ,然后你就得到了一个 N 字节data数组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) 在上面的例子中,但仅当数组是最后一个成员时。如果您要在数组之后添加另一个字段,那么数组和最后一个字段将需要具有不同的偏移量,因此零长度数组最终将是 1 字节(+对齐),只是为了具有不同地址。更不用说没有理智的编译器允许您在结构中间声明零长度数组。它实际上只能作为结构的尾部。

于 2013-06-06T21:12:22.917 回答