Question

When declaring a member variable of a templated class, is there a way to make it require zero memory depending on the value of some template parameter?

An example is to define something like std::array<T,n> that would require zero space when n==0.

For example:

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

Is there a way to eliminate the overhead of the optional_args when num_optional_args==0?

Most implementations of std::array<T,n> reserve space for one T element even when n==0.

Is there another means that would reserve zero space? Why is this not part of the C++ standard?

Was it helpful?

Solution

You could specialize your type so that optional_args doesn't exist when the number is zero. If you need the object to exist then the only way in which an object can exist and can be referred to while in fact taking up no space is through the empty base class optimization.

You might use that in the following way:

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

OTHER TIPS

You either need to reserve space for at least one element, or keep a pointer to that element. It is impossible to have an array structure which takes zero memory.

The following structure takes only one int and one pointer when created, which is about as close to zero as you'll get:

template<typename T>
class array {

  int sz;
  T *head;

};

Beyond that, the concept of requiring zero space in the class definition is goofy. Wanting it to take near-zero space when instantiated might make sense, and could be done by parameterizing the constructor as follows:

template<typename T>
class array {

  int sz;
  T *head;

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

as Praetorian said, you could specialize for 0. And if you want all variants of class C to have the same interface, you could have all C derive from C<0>, like so: template class 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;
}

If you don't care for POD-ness of your thing, you can use 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(); }
};

If, as is likely, std::array is an empty class, that should remove the overhead. But your class is no longer a POD, because compressed_pair isn't.

I truly don't remember if it is fully-legal in C++, but I think it still is in C: you can have a zero-sized array, but it must be the last member of a structure definition. Historically, it was used i.e. for variable length buffers:

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

The buf.data is fully usable array'n'pointer, so if you take care and malloc N+sizeof(int) bytes, then you can cast it to a buffer, set the size to N and here you go, you've got an N byte data array with size prefix. And the point is that every such buffer will always have and start with 'size' prefix and then have data, so you can cast every such buffer to buffer and inspect the size and then use *(data+x) or data[x] provided that 0

See for example http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

However, note that's C, not C++, but I'm almost sure I saw such raw-mem tricks in PODs in C++ too.

Aside from that, the most important point is that such array WILL have a zerobyte length. sizeof(buffer) == sizeof(int) in the above example, but ONLY if the array is the last member. If you'd add another field after the array, then the array and that last-field would need to have different offsets, so that zero-len array would end up being 1-byte (+align) just for the sake of having different address. Not mentioning that no sane compiler would allow you to declare zero-len array in the middle of the structure. It's really only possible as the tail of the struct.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top