Более простой способ установить несколько слотов массива в одно значение
Я пишу в C++, и у меня есть следующий код:
int array[30];
array[9] = 1;
array[5] = 1;
array[14] = 1;
array[8] = 2;
array[15] = 2;
array[23] = 2;
array[12] = 2;
//...
Есть ли способ инициализировать массив, подобный следующему?
int array[30];
array[9,5,14] = 1;
array[8,15,23,12] = 2;
//...
Примечание. В реальном коде может быть до 30 слотов, для которых необходимо установить одно значение.
10 ответов
Эта функция поможет сделать его менее болезненным.
void initialize(int * arr, std::initializer_list<std::size_t> list, int value) {
for (auto i : list) {
arr[i] = value;
}
}
Назови это так.
initialize(array,{9,5,14},2);
Вариант ответа Ааронмана:
template <typename T>
void initialize(T array[], const T& value)
{
}
template <size_t index, size_t... indices, typename T>
void initialize(T array[], const T& value)
{
array[index] = value;
initialize<indices...>(array, value);
}
int main()
{
int array[10];
initialize<0,3,6>(array, 99);
std::cout << array[0] << " " << array[3] << " " << array[6] << std::endl;
}
Пример: нажмите здесь
Просто для удовольствия я создал несколько иной подход, который требует немного инфраструктуры, позволяющей инициализацию, вот так:
double array[40] = {};
"9 5 14"_idx(array) = 1;
"8 15 23 12"_idx(array) = 2;
Если цифры должны быть разделены запятыми, требуется небольшое изменение. В любом случае, вот полный код:
#include <algorithm>
#include <iostream>
#include <sstream>
#include <iterator>
template <int Size, typename T = int>
class assign
{
int d_indices[Size];
int* d_end;
T* d_array;
void operator=(assign const&) = delete;
public:
assign(char const* base, std::size_t n)
: d_end(std::copy(std::istream_iterator<int>(
std::istringstream(std::string(base, n)) >> std::skipws),
std::istream_iterator<int>(), this->d_indices))
, d_array()
{
}
assign(assign<Size>* as, T* a)
: d_end(std::copy(as->begin(), as->end(), this->d_indices))
, d_array(a) {
}
assign(assign const& o)
: d_end(std::copy(o.begin(), o.end(), this->d_indices))
, d_array(o.d_array)
{
}
int const* begin() const { return this->d_indices; }
int const* end() const { return this->d_end; }
template <typename A>
assign<Size, A> operator()(A* array) {
return assign<Size, A>(this, array);
}
void operator=(T const& value) {
for (auto it(this->begin()), end(this->end()); it != end; ++it) {
d_array[*it] = value;
}
}
};
assign<30> operator""_idx(char const* base, std::size_t n)
{
return assign<30>(base, n);
}
int main()
{
double array[40] = {};
"1 3 5"_idx(array) = 17;
"4 18 7"_idx(array) = 19;
std::copy(std::begin(array), std::end(array),
std::ostream_iterator<double>(std::cout, " "));
std::cout << "\n";
}
Я просто поиграл ради веселья / экспериментов (обратите внимание на мои опасения в нижней части ответа):
Используется так:
smartAssign(array)[0][8] = 1;
smartAssign(array)[1][4][2] = 2;
smartAssign(array)[3] = 3;
smartAssign(array)[5][9][6][7] = 4;
Исходный код:
#include <assert.h> //Needed to test variables
#include <iostream>
#include <cstddef>
template <class ArrayPtr, class Value>
class SmartAssign
{
ArrayPtr m_array;
public:
class Proxy
{
ArrayPtr m_array;
size_t m_index;
Proxy* m_prev;
Proxy(ArrayPtr array, size_t index)
: m_array(array)
, m_index(index)
, m_prev(nullptr)
{ }
Proxy(Proxy* prev, size_t index)
: m_array(prev->m_array)
, m_index(index)
, m_prev(prev)
{ }
void assign(Value value)
{
m_array[m_index] = value;
for (auto prev = m_prev; prev; prev = prev->m_prev) {
m_array[prev->m_index] = value;
}
}
public:
void operator=(Value value)
{
assign(value);
}
Proxy operator[](size_t index)
{
return Proxy{this, index};
}
friend class SmartAssign;
};
SmartAssign(ArrayPtr array)
: m_array(array)
{
}
Proxy operator[](size_t index)
{
return Proxy{m_array, index};
}
};
template <class T>
SmartAssign<T*, T> smartAssign(T* array)
{
return SmartAssign<T*, T>(array);
}
int main()
{
int array[10];
smartAssign(array)[0][8] = 1;
smartAssign(array)[1][4][2] = 2;
smartAssign(array)[3] = 3;
smartAssign(array)[5][9][6][7] = 4;
for (auto i : array) {
std::cout << i << "\n";
}
//Now to test the variables
assert(array[0] == 1 && array[8] == 1);
assert(array[1] == 2 && array[4] == 2 && array[2] == 2);
assert(array[3] == 3);
assert(array[5] == 4 && array[9] == 4 && array[6] == 4 && array[7] == 4);
}
Дайте мне знать, что вы думаете, я обычно не пишу такой код, я уверен, что кто-то укажет на некоторые проблемы;)
Я не на 100% уверен в сроке жизни прокси-объектов.
Лучшее, что вы можете сделать, если ваши индексы не связаны, - это "цепочка" назначений:
array[9] = array[5] = array[14] = 1;
Однако, если у вас есть какой-то способ вычислить ваши индексы детерминистическим способом, вы можете использовать цикл:
for (size_t i = 0; i < 3; ++i)
array[transform_into_index(i)] = 1;
Этот последний пример также очевидно применим, если у вас есть контейнер, в котором хранятся ваши индексы. Так что вы могли бы сделать что-то вроде этого:
const std::vector<size_t> indexes = { 9, 5, 14 };
for (auto i: indexes)
array[i] = 1;
Компиляторам, которые все еще не поддерживают аргумент шаблона переменной и универсальный список инициализации, может быть трудно понять, что некоторые из опубликованных решений не будут работать
Похоже, что OP намеревается работать только с массивами чисел, а valarray с переменными аргументами может решить эту проблему довольно легко.
#include <valarray>
#include <cstdarg>
#include <iostream>
#include <algorithm>
#include <iterator>
template <std::size_t size >
std::valarray<std::size_t> selection( ... )
{
va_list arguments;
std::valarray<std::size_t> sel(size);
//Skip the first element
va_start ( arguments, size );
va_arg ( arguments, int );
for(auto &elem : sel)
elem = va_arg ( arguments, int );
va_end ( arguments );
return sel;
}
int main ()
{
//Create an array of 30 integers
std::valarray<int> array(30);
//The first argument is the count of indexes
//followed by the indexes of the array to initialize
array[selection<3>(9,5,14)] = 1;
array[selection<4>(8,15,13, 12)] = 2;
std::copy(std::begin(array), std::end(array),
std::ostream_iterator<int>(std::cout, " "));
return 0;
}
Используйте оператор перегрузки <<.
#include <iostream>
#include <iomanip>
#include <cmath>
// value and indexes wrapper
template< typename T, std::size_t ... Ints> struct _s{ T value; };
//deduced value type
template< std::size_t ... Ints, typename T>
constexpr inline _s<T, Ints... > _ ( T const& v )noexcept { return {v}; }
// stored array reference
template< typename T, std::size_t N>
struct _ref
{
using array_ref = T (&)[N];
array_ref ref;
};
//join _s and _ref with << operator.
template<
template< typename , std::size_t ... > class IC,
typename U, std::size_t N, std::size_t ... indexes
>
constexpr _ref<U,N> operator << (_ref<U,N> r, IC<U, indexes...> ic ) noexcept
{
using list = bool[];
return ( (void)list{ false, ( (void)(r.ref[indexes] = ic.value), false) ... }) , r ;
//return r;
}
//helper function, for creating _ref<T,N> from array.
template< typename T, std::size_t N>
constexpr inline _ref<T,N> _i(T (&array)[N] ) noexcept { return {array}; }
int main()
{
int a[15] = {0};
_i(a) << _<0,3,4,5>(7) << _<8,9, 14>( 6 ) ;
for(auto x : a)std::cout << x << " " ;
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
//result: 7 0 0 7 7 7 0 0 6 6 0 0 0 0 6
double b[101]{0};
_i(b) << _<0,10,20,30,40,50,60,70,80,90>(3.14)
<< _<11,21,22,23,24,25>(2.71)
<< _<5,15,25,45,95>(1.414) ;
}
Я помню, для статической инициализации существует такой синтаксис, как:
int array[30] = {
[9] = 1, [8] = 2
}
И так далее. Это работает в gcc, про другие компиляторы - я не знаю.
struct _i_t
{
int * array;
struct s
{
int* array;
std::initializer_list<int> l;
s const& operator = (int value) const noexcept
{
for(auto i : l )
array[i] = value;
return *this;
}
};
s operator []( std::initializer_list<int> i ) const noexcept
{
return s{array, i};
}
};
template< std::size_t N>
constexpr _i_t _i( int(&array)[N]) noexcept { return {array}; }
int main()
{
int a[15] = {0};
_i(a)[{1,3,5,7,9}] = 7;
for(auto x : a)std::cout << x << ' ';
}
Любые ваши хитрые трюки будут развернуты компилятором / ассемблером именно в том, что у вас есть. Вы делаете это для удобства чтения? Если ваш массив уже является init, вы можете сделать:
array[8] = array[15] = array[23] = array[12] = 2;
Но я подчеркиваю свою точку зрения выше; это будет преобразовано в то, что у вас есть.