В новом стандарте в библиотеке появилась безопасная обёртка вокруг встроенного массива std::array. Сделано вроде как просто и эффективно. Даже инициализировать его можно так же как встроенный массив.
Всё бы хорошо, но есть одна проблема. Этот шаблонный класс требует два параметра: тип хранимого значения и размер. И если я хочу например передать array в фукнцию, даже по ссылке или указателю, я должен в типе аргумента функции указать оба параметра шаблона: тип и размер.
Пример:
#include <iostream>
#include <array>
void f(const std::array<int, 5>& x) {
for (int y : x)
std::cout << y << ", ";
std::cout << '\n';
}
int main() {
std::array<int,5> a={1,2,3};
f(a);
return 0;
}
Это очень неудобно: нужно плодить волшебную константу везде, где используется массив и заменять ее ручками когда в массив добавляется ещё одно значение. При этом в функции эта константа никак не используется.
Подскажите пожалуйста, можно ли как-то обойти эту проблему?
Здравствуйте, Андрей Е, Вы писали:
АЕ>Подскажите пожалуйста, можно ли как-то обойти эту проблему?
1. Определить именованную константу.
2. Определить свой тип массива, определяющего размерность.
Здравствуйте, Андрей Е, Вы писали:
АЕ>Это очень неудобно: нужно плодить волшебную константу везде, где используется массив и заменять ее ручками когда в массив добавляется ещё одно значение. При этом в функции эта константа никак не используется.
АЕ>Подскажите пожалуйста, можно ли как-то обойти эту проблему?
Ты думаешь, когда ты делаешь "for (auto y: x)", откуда этот цикл знает, сколько элементов в массиве x? Этот цикл эквивалентен:
for (auto it = x.begin(), end = x.end(); it != end; ++it) { auto y = *it; ... }
А уж 'x.end() === x.begin() + 5'. Так что число элементов, там используется все таки.
Если хочешь, чтобы твой код не зависел от числа элементов, передавай в функции итераторы на начало и конец:
Здравствуйте, Андрей Е, Вы писали:
АЕ>Подскажите пожалуйста, можно ли как-то обойти эту проблему?
Можно.
Определяем велосипед:
template<class... T> struct _Len;
template<class T, class... TT> struct _Len<T,TT...>
{ static const std::size_t value = 1 + _Len<TT...>::value; };
template<class T> struct _Len<T>
{ static const std::size_t value = 1; };
template<class T, class... A> inline
std::array<T,_Len<A...>::value> arrayof(A&&... a)
{ std::array<T,_Len<A...>::value> r = { a... }; return r; }
Используем:
template<class T, std::size_t N>
void f(const std::array<T,5>& x) {
for (T y : x)
std::cout << y << ", ";
std::cout << '\n';
}
int main()
{
auto arr = asarray<int>(1,2,3);
f(arr);
f( asarray<insigned short>(4,5,6) );
}
ЗЫ. И, разумеется, Visual C++ sucks. GCC rulez, ибо шаблоны с переменным числом аргументов.
ЗЗЫ. Конечно, можно отсосать у Visual C++ и написать кучу перегруженных asarray(), но мне лень а вам — неплохое задание для общего развития
__________
16.There is no cause so right that one cannot find a fool following it.
Здравствуйте, Андрей Е, Вы писали:
АЕ>В новом стандарте в библиотеке появилась безопасная обёртка вокруг встроенного массива std::array. Сделано вроде как просто и эффективно. Даже инициализировать его можно так же как встроенный массив. АЕ>Всё бы хорошо, но есть одна проблема. Этот шаблонный класс требует два параметра: тип хранимого значения и размер. И если я хочу например передать array в фукнцию, даже по ссылке или указателю, я должен в типе аргумента функции указать оба параметра шаблона: тип и размер. АЕ>Пример: АЕ>
АЕ>Это очень неудобно: нужно плодить волшебную константу везде, где используется массив и заменять ее ручками когда в массив добавляется ещё одно значение. При этом в функции эта константа никак не используется.
АЕ>Подскажите пожалуйста, можно ли как-то обойти эту проблему?
Вы можете работать с ним точно также, как вы работаете с массивами в С, то есть испольуя указатель на первый элемент массива, который получается с помощью функции члена класса data, и размер массива, который получается с помощью функции члена класса size.
например,
#include <iostream>
#include <array>
#include <algorithm>
void f( const int a[], size_t n )
{
std::for_each( a, a + n,
[]( int x ) { std::cout << x << ", "; } );
std::cout << std::endl;
}
int main()
{
std::array<int,5> a={1,2,3};
f( a.data(), a.size() );
return 0;
}
Здравствуйте, 0xDEADBEEF, Вы писали:
DEA>Здравствуйте, Андрей Е, Вы писали:
DEA>template<class T, class... TT> struct _Len<T,TT...> DEA> { static const std::size_t value = 1 + _Len<TT...>::value; };
правильно ли я понимаю, что здесь определяется специализация для головы списка типов, и остальной части?
а какая тогда будет вызвана специлизация здесь _Len<TT...>::value ? как оно понимает что надо выбрать вторую специализацию?
Здравствуйте, 0xDEADBEEF, Вы писали:
DEA>Определяем велосипед:
(skipped)
Зачем Len, когда есть sizeof...? Workaround для какой-то версии компилятора?
// static_cast к типу массива - чтобы не ругался, когда мы int -> short int присваиваем,
// T внутри static_cast менять, вроде, не надо,
// а дополнительные скобки - так, чтобы мой gcc не ругался warning'ми:template<class T, class... A>
inline std::array<T,sizeof...( A )> arrayof(A&&... a) {
return {{ static_cast<T>( a )... }};
}
Здравствуйте, Alexey F, Вы писали:
AF>Зачем Len, когда есть sizeof...? Workaround для какой-то версии компилятора?
Если честно, наличие вариадик sizeof как-то пробежало мимо внимания. А ручонки сами по себе накропали рекурсивный шаблон
Спасибо за напоминание.
AF>// static_cast к типу массива - чтобы не ругался, когда мы int -> short int присваиваем,
//Тогда, лучше std::forward, коль скоро мы rvalue references используем и украсим это дело constexpr.
//Он тут, конечно, как собаке пятая нога, но красявоtemplate<class T, class... A> inline
constexpr array<T,sizeof...( A )> arrayof(A&&... a)
{ return {{ std::forward<T>( a )... }}; }
__________
16.There is no cause so right that one cannot find a fool following it.