Есть ли способ передать анонимный массив в качестве аргумента в С++?
Я хотел бы объявить массив как аргумент функции в С++, как показано в приведенном ниже примере кода (который не компилируется). Есть ли способ сделать это (кроме объявления массива отдельно заранее)?
#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;
}
Ответы
Ответ 1
Если вы используете более старые варианты С++ (pre-С++ 0x), это недопустимо. "Анонимный массив", на который вы ссылаетесь, на самом деле является списком инициализаторов. Теперь, когда С++ 11 отсутствует, это можно сделать со встроенным типом initializer_list
. Вы теоретически можете использовать его как список инициализаторов стиля C, используя extern C
, если ваш компилятор анализирует их как C99 или более поздних версий.
Например:
int main()
{
const int* p;
p = (const int[]){1, 2, 3};
}
Ответ 2
Это разрешено с помощью typecast в С++ 11 и extern "C"
с C99:
void PrintArray(size_t len, const int *array)
{
for(size_t i = 0; i < len; i++)
printf("%d\n", array[i]);
}
int main(int argc, char **argv)
{
PrintArray(5, (const int[]){1, 2, 3, 4, 5});
return 0;
}
Ответ 3
Это компилируется, но я бы не рекомендовал его.
#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;
}
Ответ 4
Отказ от ответственности: для этого ответа я получаю некоторые downvotes, но он начинается с 2009 года, где С++ 11 должен был быть определен. Для современного С++ прокрутите страницу ниже.
Ну, попробуйте использовать boost...
Вот решение, использующее библиотеку boost:: assign и бит больше С++, как программирование;)
#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;
}
С++ 11 и выше с объяснением особенностей
Соответствует clang:
clang++ -std=c++14 -I /usr/local/include/ main.cpp
#include <boost/assign/list_of.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <initializer_list>
template<typename CollectionT, typename OStream>
auto // <- auto result type deduction from C++ 14
make_output_iterator(CollectionT const& coll, OStream& out)
{
return std::ostream_iterator<typename CollectionT::value_type>(out, ", ");
}
// here template specialization is used, to demonstrate initializer lists from C++ 11
template<typename T>
void print(std::initializer_list<T> items)
// ^----------------------^ passed by value due to move semantics
{
using namespace std;
cout << "printing an initializer list: ";
copy(items.begin(), items.end(), make_output_iterator(items, cout));
cout << endl;
}
template<typename CollectionT>
void print(CollectionT const& items)
{
using namespace std;
cout << "printing another collection type: ";
copy(items.begin(), items.end(), make_output_iterator(items, cout));
cout << endl;
}
int main()
{
print({0,1,2,3,4,5,6,7,9});
using namespace boost::assign;
print( list_of(0)(1)(2)(3)(4)(5)(6)(7)(8)(9) );
}
Ответ 5
Да и нет. В текущей версии стандарта (ISO С++ 1998 с изменениями от 2003 г.) это невозможно. Однако в следующей версии стандарта "С++ 0x" (который, несмотря на его название, подразумевающий, что он будет выпущен в 200 раз, скорее всего, будет выпущен в 2010 году), это будет возможно с помощью std:: initializer_list < gt;;.
Ответ 6
С С++ 0x вы можете использовать std::initializer_list
(и цикл foreach)
#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 });
}
Ответ 7
Вы можете использовать переменное количество аргументов вместо передачи массива:
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);
}
Я не скомпилировал это, поэтому я, вероятно, ошибся или два, но, надеюсь, он достаточно близко. Посмотрите va_start для справки.
Ответ 8
Вы можете использовать список std:: initializer_list, предложенный Джо D, однако я не уверен, что вы можете использовать его, если хотите другие параметры. (Я не могу найти никакой информации об этом.) Однако, если вы объявляете ссылку на const для вектора, файл initializer_list, созданный с помощью {...}, преобразуется в вектор.
#include <iostream>
#include <vector>
void PrintArray(const char* prefix, const std::vector<int>& array)
{
std::cout << prefix << std::endl;
for (int i : array) {
std::cout << i << std::endl;
}
}
int main(int, char **)
{
PrintArray("test array", {5,6,7,8,9} );
return 0;
}
Ответ 9
Начиная с С++ 11, вы можете просто использовать std::begin(std::initializer_list const&)
для получения указателя. Пример:
#include <iostream>
void func(int len, const int* x)
{
for(int i=0;i<len;++i)
std::cout << x[i] << "\n";
}
int main()
{
func(5, std::begin({1,3,6,823,-35}));
}
В отличие от принятого ответа, это действительно стандартный совместимый код.
Ответ 10
не делать.
Вместо этого используйте вектор.
Ответ 11
Еще один выбор - использовать массив в библиотеке TR1, который, вероятно, станет частью следующего стандарта и будет поддерживаться многими компиляторами.
#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);
}
Ответ 12
Вы можете сделать это в ANSI-C, используя хороший ol va_list
Templatized С++ с std::vector
возвращен для удобства дополнительно
#include <stdarg.h>
#include <vector>
using namespace std ;
struct Point
{
int x,y;
Point():x(0),y(0){}
Point( int ix, int iy ):x(ix),y(iy){}
~Point(){printf("%d %d - the point has been destroyed!\n",x,y);}
void print(){printf("%d,%d\n",x,y);}
} ;
// Concrete example using a list of int
int* initFrom( int numItems, ... )
{
int* list = new int[ numItems ] ;
va_list listPointer;
va_start( listPointer, numItems );
for( int i = 0 ; i < numItems; i++ )
list[ i ] = va_arg( listPointer, int ) ;
return list ;
}
// templatized version.
template <typename T> vector<T> initFrom( int numItems, ... )
{
vector<T> list ;
list.resize( numItems ) ;
va_list listPointer;
va_start( listPointer, numItems );
for( int i = 0 ; i < numItems; i++ )
list[ i ] = va_arg( listPointer, T ) ;
return list ;
}
int main()
{
int* theList = initFrom( 4, 900, 2000, 1000, 100 ) ;
for( int i = 0 ; i < 4 ; i++ )
printf( "Item %d=%d\n", i, theList[i] );
puts( "\n\n--Lots of destruction using non-ptr" ) ;
vector<Point> thePoints = initFrom<Point>( 3, Point(3,7), Point(4,5), Point(99,99) ) ;
puts( "Our listing:" ) ;
for( int i = 0 ; i < 3 ; i++ )
thePoints[i].print() ;
puts( "\n\n-- Less destruction using ptr" ) ;
// Be careful of extra copy construction. Using a vector of pointers
// will avoid that
vector<Point*> theNewPoints = initFrom<Point*>( 3, new Point(300,700), new Point(400,500), new Point(990,990) ) ;
puts( "Our listing:" ) ;
for( int i = 0 ; i < 3 ; i++ )
theNewPoints[i]->print() ;
puts( "END OF PROGRAM --" ) ;
}
Ответ 13
Здесь простое чистое решение для получения массива C-стиля, о котором никто не упомянул:
#include <iostream>
using namespace std;
template <int N>
int
get_last( const int ( &my_array )[N] ) {
return my_array[N-1];
}
int main()
{
cout << "Last: " << get_last( { 1, 2, 3, 55 } );
return 0;
}
Ответ 14
Нет, и это плохое кодирование практике в любом случае. Просто объявите const int* foo = {5,6,7,8,9};
перед вызовом функции. Даже если это действительно сработало, это не ускорит вашу программу или время компиляции. Значения все равно должны быть выделены в памяти и переданы через вызов функции.