views:

456

answers:

6

I'd like to use boost::array as a class member, but I do not know the size at compile time. I thought of something like this, but it doesn't work:

int main() {
    boost::array<int, 4> array = {{1,2,3,4}};
    MyClass obj(array);
}

class MyClass {
    private:
        boost::array<int, std::size_t> array;
    public:
        template<std::size_t N> MyClass(boost::array<int, N> array)
        : array(array) {};
};

The compiler, gcc, says:

error: type/value mismatch at argument 2 in template parameter list for
  ‘template<class _Tp, long unsigned int _Nm> struct boost::array’
error:   expected a constant of type ‘long unsigned int’, got ‘size_t’

Which obviously means that one cannot use variable-sized arrays as class members. If so, this would negate all the advantages of boost::array over vectors or standard arrays.

Can you show me what I did wrong?

+1  A: 

You're wrong about the error :

    template<unsigned long N> MyClass(boost::array<int, N> array) : array(array) {};

Should work. By the way this will still generate the array at compile time so it's not a good solution. And other errors will occur.

Here what you want is a vector, with a reserved size and some assertions that keep the capacity on a fixed size.

Klaim
The error mentioned is about `boost::array<int, std::size_t>`, and he seems to interpret it correctly.
Roger Pate
+3  A: 

No, boost::array (it's in TR1 as std::tr1::array) is a statically sized buffer. The point of the class is to avoid a dynamic memory allocation - you can put a boost::array entirely on the stack.

You can make your example class take a template int and pass that to the boost::array member,

template<int Size>
class MyClass
{
private:
    boost::array<int, Size> m_array;
public:
    // ....
};

But that is just dressing, it's still a static allocation.

Terry Mahaffey
Note that this solution moves the problem to class MyClass (where each template instantiation of it will become a different type). Depending on the actual task of the questioner this may work, but I doubt it.
frunsi
Also "it is" no allocation, the phrase is misleading.
frunsi
That's what I meant by it just being "dressing", and static sized allocations (and stack allocations) are still allocations.
Terry Mahaffey
+4  A: 

Boost's array is fixed-size based on the second template parameter, and boost::array<int,4> is a different type from boost::array<int,2>. You cannot have instances of the same class (MyClass in your example) which have different types for their members.

However, std::vectors can have different sizes without being different types:

struct MyClass {
  template<std::size_t N>
  explicit
  MyClass(boost::array<int, N> const& array)
  : data(array.begin(), array.end())
  {}

private:
  std::vector<int> data;
};

int main() {
  boost::array<int, 4> a = {{1,2,3,4}};
  MyClass obj(a);

  boost::array<int, 2> a2 = {{42,3}};
  MyClass obj2(a2);

  // notice obj.data.size() != obj2.data.size()

  return 0;
}

That said, boost::array is still useful (it's even useful in this example code), just not in the exact way you want to use it.

Roger Pate
+3  A: 

May I suggest using boost::scoped_array instead? On the other hand, you might not want to actually copy the whole array every time. Then boost::shared_array would be a better choice.

Nikolai N Fetissov
A: 

Though you already accepted an answer, please note that std::vector may not be the right choice for your task. If you want to create the array once - a fixed size array - and you do not want to resize it later on, then a good old plain array may be the right choice for you! Ignore boost::array, ignore std::vector, its intents are very different, keep it simple. KISS, YAGNI and so on...

int main() {
    int* array = new int[4];
    for( int i=0; i<4; ++i ) array[i] = i+1;
    MyClass obj(array);
}

class MyClass {
    private:
        int* array;
    public:
        MyClass( int* array )
        : array(array) {}
        ~MyClass() { delete[] array; }
};

EDIT: As Nikolai N Fetissov already stated, boost::scoped_array might be a good choice. It provides a thin RAII wrapper around the array. Here is a usage example (hope it is correct, feel free to edit otherwise):

class MyClass {
    private:
        boost::scoped_array<int> array;
    public:
        MyClass( int* array )
        : array(array) {}
};
frunsi
Hmm, missing copy constructor and copy assignment operator here.
Nikolai N Fetissov
boost::array/tr1::array is a very thin wrapper around a fixed size array - in fact, it has zero overhead. There is literally no reason to use a C-style array when tr1::array is available.
Terry Mahaffey
Terry: have you read the actual question? No reason to down-vote me, if you did not understand the intent of the questioner.
frunsi
Terry: The "C-style arrays", as you call them, are a perfect fit when the size of the array is unknown at compile-time. tr1/boost::array requires that the size is known at compile time, so it is out of question. THAT is the point.
frunsi
+3  A: 

You're missing some basic points. You can have:

  1. A statically allocated array - char arr[10];
  2. A dynamically allocated array - char* arr = new arr[10];

The first one's size is known during compile time (because the size is a constant), hence you can preallocate a memory space for it, the other one isn't, hence you need to allocate memory for it during run-time.

STL/TR1/Boost provides wrappers for both types of arrays. Those are not only wrappers for convieniece, but also for safety (range checking in some situations) and power (iterators). For both cases we have a separate wrapper:

  1. Statically allocated array wrapper boost::array<char,10> arr;
  2. Dynamically allocated array wrapper std::vector<char> arr;

The latter has the benefit of being self resizing, and allowing resizing, in addition to being dynamically allocatable. boost::array on the other hand, mimics a type arr[const] construct.

Hence, you need to decide whether you want the class to have statically allocated memory, or dynamically. The former, only makes sense if the classes storage is either fixed size, or one of a few fixed sizes. The latter makes sense in all other cases.

Statically allocated would use templates

template < size_t N >
class MyClass {
private:
    boost::array< int, N > array;
public:
   MyClass(boost::array< int, N > array) : array(array) {};
};

// ...

boost::array<int, 4> array = {{1,2,3,4}};
MyClass<4> obj(array);

But would create separate code for every size of the class, and they would not be inter-operable (this can be circumvented though).

Dynamically allocated would use vectors

class MyClass {
private:
    std::vector< int > array;
public:
   MyClass(const std::vector< int >& array) : array(array) {};
};

Don't be afraid of vectors, treat them as dynamically allocated arrays -- the resizingness of vectors is an added benefit that doesn't affect performance almost at all.

Kornel Kisielewicz