views:

920

answers:

10

Hi all,

I'd like to be able to declare an array as a function argument in C++, as shown in the example code below (which doesn't compile). Is there any way to do this (other than declaring the array separately beforehand)?

#include <stdio.h>

static void PrintArray(int arrayLen, const int * array)
{
   for (int i=0; i<arrayLen; i++) printf("%i -> %i\n", i, array[i]);
}

int main(int, char **)
{
   PrintArray(5, {5,6,7,8,9} );  // doesn't compile
   return 0;
}
+13  A: 

No. This is not allowed by the standard. The "anonymous array" you refer to is actually an initializer list. Initializer lists will be formally recognized as objects in C++0x. For now, they are just a syntax for initializing c-arrays and POD types.

For example, the following also does not work:

int main()
{
    int* p;
    p = {1, 2, 3}; // not valid C or C++
}
rlbond
GCC is actually okay with this, as long as you typecast the literal: p = (int[]){1, 2, 3}; as per Adam's answer way down below.
Chris Burt-Brown
That's true, but just to reiterate for other readers, it is not portable c++.
rlbond
+4  A: 

Yes and no. In the current version of the standard (ISO C++ 1998 with ammendments from 2003), it is not possible. However, in the next version of the standard "C++0x" (which, despite its name implying that it will be released in 200x, will most likely be released in 2010), it will be possible with std::initializer_list<>.

Michael Aaron Safyan
A: 

No, and it's bad coding practice anyway. Just declare const int* foo = {5,6,7,8,9}; before the function call. Even if this did work, it wouldn't speed up your program or compile time. The values would still need to be allocated in memory and passed through the function call.

bkritzer
There's nothing about using initialiser lists that is "bad coding practice", as long as you use them where appropriate.
Greg Hewgill
You're right. Not quite sure why I put that in there.
bkritzer
The intent isn't to speed up compile time or run time, it's to improve encapsulation. In particular, I have a base class whose constructor takes an array of strings, and the subclass's constructor knows what the array should be. Without being able to pass them directly to the superclass constructor, things get a bit awkward (e.g. I have to declare the array as a static variable)
Jeremy Friesner
+12  A: 

This compiles, but I wouldn't recommend it.

#include <stdio.h>

struct arr
{
   int array[5];
};

static void PrintArray(int arrayLen, arr array)
{
   for (int i=0; i<arrayLen; i++) printf("%i -> %i\n", i, array.array[i]);
}

int main(int, char **)
{
   PrintArray(5, (arr){5,6,7,8,9});
   return 0;
}
niteria
and vim gets confused :)
niteria
That's clever - for extra hackiness you could combine this with the fact that as the last element of a struct, arr::array doesn't have to have an explicit dimension (which would allow this to be used with any size array), and maybe make arr a template, which would allow it to be used with any type.
Tyler McHenry
Agreed, this is clever. I don't think you'd want to do this, but certainly +1 for cleverness.
rlbond
@Tyler, doesn't compile without an explicit dimension. You can put the array length in a template parameter though.
bdonlan
@Tyler: or you could just use a vector.
rlbond
This compiles in what? This is not standard, at least in C++ <= '03.
Richard Corden
I tried it on gcc 4.1.2
niteria
@niteria: Ok. Anyway, it's not standard C++ '98 or '03. Comeau for example fails to compile it.
Richard Corden
+2  A: 

You could use a variable number of arguments instead of passing an array:

static void PrintArray(int arrayLen, ...)
{
   int this_value;
   va_list array;

   va_start(array, arrayLen);
   for (int i=0; i<arrayLen; i++) 
   {
     this_value = va_arg(array, int);
     printf("%i -> %i\n", i, this_value);
   }
   va_end(array);
}

I didn't compile this so I probably made a mistake or two, but hopefully it's close enough. Look up va_start for reference.

Darryl
+2  A: 

Don't. Use a vector instead.

Tim Williscroft
+4  A: 

Well, try using boost...

Here is the solution using the boost::assign library and bit more C++ like programming ;)

#include <boost/assign/list_of.hpp>

#include <iostream>
#include <algorithm>


namespace
{
  template<class CollectionT>
  void print(CollectionT const& coll)
  {
    std::ostream_iterator<int> out(std::cout, ", ");
    std::copy(coll.begin(), coll.end(), out);
  }
}



int main()
{
  using namespace boost::assign;

  print( list_of(1)(2)(3)(4)(5) );

  return 0;
}

Hope that helps,
Ovanes

ovanes
+2  A: 

As others have mentioned, it will be allowed in C++0x. However, it is currently allowed in C99:

void PrintArray(size_t len, int *array)
{
    for(size_t i = 0; i < len; i++)
        printf("%d\n", array[i]);
}

int main(int argc, char **argv)
{
    PrintArray(5, (int[]){1, 2, 3, 4, 5}); /* C99 only, not valid in C++ or ANSI C */
    return 0;
}
Adam Rosenfield
+1  A: 

Another choice would be to use array in the TR1 library which is likely to become part of the next standard and is supported by many compilers.

#include <array>
#include <algorithm>
#include <iostream>

using std::tr1::array;
using std::cout;
using std::copy;
using std::ostream_iterator;

template <class Container>
void PrintArray(Container &values)
{
  copy(values.begin(), values.end(), ostream_iterator<int>(cout, "\n"));
}

int main()
{
  array<int, 5> values = {1, 2, 3, 4, 5};
  PrintArray(values);
}
If he wanted to create a local variable, he'd just: int values[] = {1,2,3,4,5}; PrintArray(values);I think he wants to do it in a single line.
keraba
+1 because you can create an anonymous array using this: `array<int,5>{1, 2, 3, 4, 5});`
Joe D
A: 

With C++0x you could use an std::initializer_list (and a foreach loop)

#include <iostream>
#include <initializer_list>

void print (const std::initializer_list<int>& array)
{
    for (auto x : array) // C++0x foreach loop
        std::cout << x << std::endl;
}

int main (int argc, char ** argv)
{
    print ({ 1, 2, 3, 4, 5 });
}

Joe D
You should probably take the list by (const-)reference. Also, you're missing the 'r' at the end of 'initializer'. :)
GMan
Changed it to const reference and added the 'r' at the end of initalizer.
Joe D