doc07b5
doc07b5

Reputation: 630

Member array with size defined by template parameter, but why no warning for zero-size array?

I was trying to write a templated base class to store a fixed number of data types, each with varying length. Here is a simplified version of much what I was trying to do:

template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
   public:

  EncapsulatedObjectBase();

  ~EncapsulatedObjectBase();

  double m_real[NR0];
  int m_int[NINT];
}

Yeah...so the template parameters can be zero, thus declaring a zero-length array of objects. There will be multiple derived classes for this base, each defining their own number of variables. I have two questions:

1) Is this approach fundamentally flawed?

2) If so...why doesn't icc13 or gcc4.7.2 give me warnings about this when I instantiate a zero-length array? For gcc I use -wall and -wextra -wabi. The lack of warnings made me think that this sort of thing was OK.

EDIT:

Here is the contents of a file that show what I am talking about:

#include <iostream>

template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
  EncapsulatedObjectBase(){}
  ~EncapsulatedObjectBase(){}

  double m_real[NR0];
  int m_int[NINT];
};


class DerivedDataObject1 : public EncapsulatedObjectBase<2,0>
{
   public:

   DerivedDataObject1(){}

  ~DerivedDataObject1(){}

  inline int& intvar1() { return this->m_int[0]; }
  inline int& intvar2() { return this->m_int[1]; }

};


class DerivedDataObject2 : public EncapsulatedObjectBase<0,2>
{
   public:

   DerivedDataObject2(){}

  ~DerivedDataObject2(){}

  inline double& realvar1() { return this->m_real[0]; }
  inline double& realvar2() { return this->m_real[1]; }
};




int main()
{
   DerivedDataObject1 obj1;
   DerivedDataObject2 obj2;

   obj1.intvar1() = 12;
   obj1.intvar2() = 5;

   obj2.realvar1() = 1.0e5;
   obj2.realvar2() = 1.0e6;

   std::cout<<"obj1.intvar1()  = "<<obj1.intvar1()<<std::endl;
   std::cout<<"obj1.intvar2()  = "<<obj1.intvar2()<<std::endl;
   std::cout<<"obj2.realvar1() = "<<obj2.realvar1()<<std::endl;
   std::cout<<"obj2.realvar2() = "<<obj2.realvar2()<<std::endl;


}

If I compile this with "g++ -Wall -Wextra -Wabi main.cpp" I get no warnings. I have to use the -pedantic flag to get warnings. So I still don't know how unsafe this is. In retrospect, I feel as though it must not be a very good idea...although it would be pretty useful if I could get away with it.

Upvotes: 5

Views: 19999

Answers (3)

SergV
SergV

Reputation: 1277

1) Add to declaration of your class C++11 static_assert or BOOST_STATIC_ASSERT and you will have compile-time diagnostic for zero length array:

....
   BOOST_STATIC_ASSERT(NR0 > 0);
   BOOST_STATIC_ASSERT(NINT > 0);
   double m_real[NR0];
   int m_int[NINT];
};

2) Use std::array or boost::array and you will have run-time diagnostic (in debug mode) for index overflow problem in such code:

   BOOST_STATIC_ASSERT(NR0 > 0);
   BOOST_STATIC_ASSERT(NINT > 0);
   boost::array<double, NR> m_real;   //double m_real[NR0];
   boost::array<int, NINT> m_int;     //int m_int[NINT];
};

Remark: class boost::array has specialisation for zero-size array

3) Use size_t but not int for size of array.

Your design is quite dangerous:

   DerivedDataObject1 a;
   a.m_real[2] = 1;   // size of m_real == 0 !!!

I think it will better to change design of your class EncapsulatedObjectBase. May be it will better to use:

   template<typename T, size_t N> class EncapsulatedObjectBase
   {
    ....
   };
   class DerivedDataObject1 : public EncapsulatedObjectBase<int,2>
   {
     ....
   };
   class DerivedDataObject2 : public EncapsulatedObjectBase<double,2>
   {
     ....
   };
   class DerivedDataObject3 : public EncapsulatedObjectBase<double,2>
                            , public EncapsulatedObjectBase<int,2>
   {
     ....
   };

Upvotes: 1

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385385

Zero-sized arrays are actually illegal in C++:

[C++11: 8.3.4/1]: [..] If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero. The constant expression specifies the bound of (number of elements in) the array. If the value of the constant expression is N, the array has N elements numbered 0 to N-1, and the type of the identifier of D is “derived-declarator-type-list array of N T”. [..]

For this reason, your class template cannot be instantiated with arguments 0,0 in GCC 4.1.2 nor in GCC 4.7.2 with reasonable flags:

template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
   public:

  EncapsulatedObjectBase();

  ~EncapsulatedObjectBase();

  double m_real[NR0];
  int m_int[NINT];
};

int main()
{
   EncapsulatedObjectBase<0,0> obj;
}

t.cpp: In instantiation of 'EncapsulatedObjectBase<0, 0>':
t.cpp:17: instantiated from here
Line 10: error: ISO C++ forbids zero-size array
compilation terminated due to -Wfatal-errors.

clang 3.2 says:

source.cpp:10:17: warning: zero size arrays are an extension [-Wzero-length-array]

(Note that, in any case, you won't get any error until you do try to instantiate such a class.)

So, is it a good idea? No, not really. I'd recommend prohibiting instantiation for your class template when either argument is 0. I'd also look at why you want to have zero-length arrays and consider adjusting your design.

Upvotes: 3

Lily Ballard
Lily Ballard

Reputation: 185831

In C using a zero-sized array as the last member of a struct is actually legal and is commonly used when the struct is going to end up with some sort of dynamically-created inline data that's not known at compile-time. In other words, I might have something like

struct MyData {
    size_t size;
    char data[0];
};

struct MyData *newData(size_t size) {
    struct MyData *myData = (struct MyData *)malloc(sizeof(struct MyData) + size);
    myData->size = size;
    bzero(myData->data, size);
    return myData;
}

and now the myData->data field can be accessed as a pointer to the dynamically-sized data

That said, I don't know how applicable this technique is to C++. But it's probably fine as long as you never subclass your class.

Upvotes: 3

Related Questions