passing an array directly

This topic is 3952 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

Let's say I have the following function: void print_array(int length, const double* array); This works: double some_array[] = {0, 1, 2, 3, 4}; print_array(5, some_array); Is it possible to do something like this? print_array(5, {0, 1, 2, 3, 4}); Thanks.

Share on other sites
Moved to For Beginners.

Share on other sites
Not directly, no. You can use "temporaries" of an object type (by specifying a constructor call) or of numeric type (by just typing the constant) or const char* (by typing the string literal - of course there will always be a \0 at the end), but not of an array type. You could wrap the array in an object, but the {} syntax still can't be used for a temporary object (even if it's a POD struct which would allow that kind of initialization):

struct fiveDoubles {  double x[5];};fiveDoubles foo = {1,2,3,4,5};void print_array(int length, const double* array);print_array(5, &(foo.x)); // ok, but no way to do it with a temporary

We could of course modify print_array to take a fiveDoubles instance as well, but that doesn't address the fundamental problem. (Of course, having done that, the 'length' parameter becomes useless as well, since we've restricted ourself to "arrays' of length 5.)

Discussion from here on is C++ specific. The above also applies to C.

We could also create a temporary and use it like "print_array(fiveDoubles());", but so far we only have the default constructor, which isn't very useful.

If we do, in fact, only need "arrays" of a specific size, then the obvious solution would be to add a constructor taking all the elements:

struct fiveDoubles {  double x[5];  fiveDoubles(double a, double b, double c, double d, double e) {    // You can't initialize arrays with an initialization list :(    x[0] = a; x[1] = b; x[2] = c; x[3] = d; x[4] = e;  }};print_array(fiveDoubles(1,2,3,4,5));

But if we don't want to lose that flexibility, we'll want something more sophisticated.

Enter the standard library. Naturally, the C++ way to deal with "arrays" of unknown size - while sidestepping all that memory management nonsense - is to just use std::vector:

typedef std::vector<double> array;void print_array(const array& v);// It is possible to initialize a std::vector from an array, and because a // vector is a class type, we can make temporaries:double foo[5] = {1, 2, 3, 4, 5};print_array(array(foo, foo + sizeof(foo) / sizeof(foo[0])));

Now we can handle any size thing and we *still* don't need a separate length parameter, because that information is wrapped up within the vector. (A good policy in C code anyway, BTW: you can make structures to bind a length count to an array-allocation, and that helps keep your data organized, your parameter counts down and your code easier to manage.) But we still can't create a *useful* temporary *in one step*, because we're still wrapping up an array that we had to create separately. (Also, we're duplicating the array data.) Right?

Well... there are ways ;) I'm going to be responsible and suggest that you first look at boost::assign. But in case you can't make that work for you, here's a homemade solution... (WARNING, not tested... but I'm *pretty* sure I didn't do anything dangerous)

What I'm going to do is create another class, whose purpose in life is to create vectors. In C++, objects can behave like functions (by overloading the operator()), and I'm going to rely on that - along with an idiom called "operator chaining" (this is the way that things like "cout << foo << bar" work) to make things work. The idea is that I'll construct the object with a parameter which is the first element of the vector, and 'call' the object with each subsequent parameter - the net result being that every element is in ()'s after the class name - and finally 'call' the object again with no parameter, and interpret that to mean that we're done (i.e., that call will return the vector, instead of continuing to "chain" by returning a reference-to-self).

template <typename T>class vector_creator {  std::vector<T> created;  public:  vector_creator(const T& first) { created.push_back(first); }  vector_creator& operator()(const T& next) { created.push_back(next); }  // We "const-overload" our operator(), which is an "accessor": the idea is  // that we can safely promise that the vector_creator will not change if and  // only if the calling code promises not to change the vector.  const std::vector<T>& operator()() const { return created; }  std::vector<T>& operator()() { return created; }};// Now we can use it like:print_array(vector_creator<double>(1)(2)(3)(4)(5)());

Ok, but we're not done yet. The naming is a bit awkward, and also it requires us to explicitly state the element type for the template. Also, there's no way to construct an *empty* vector, which we may need sometimes. (We could put back a default constructor, but then the syntax would seem inconsistent when using it - we'd need two ()()'s at the end of our empty "list of elements".) But we can get around these issues by adding free functions. This "wrap a constructor in a free function" idiom is common in C++ (see std::make_pair) and quite powerful.

template<typename T>vector_creator<T> make_vector(const T& t) {  return vector_creator<T>(t);}template<typename T>std::vector<T> make_vector() {  return std::vector<T>();}// The first element has to be a double, or else we get the int template.print_array(make_vector(1.0)(2)(3)(4)(5)());// We still have to specify the template type with an empty vector, since there// are no elements from which to deduce the type.print_array(make_vector<double>());

You could also forgo the second free function and just use the empty-vector creation inline in those cases. You could also use a conversion operator instead of a no-arg operator() in the class.