I have a large std::vector<int> a, but I would like to work only on a subset of it. The idea was to create a std::vector<reference_wrapper<int> > refa that only contains the said subset (in the mwe, all elements 1< a < 4). I would then like to pass refa to functions that expect a std::vector<int> or std::vector<int>& as arguments (because I also want to use them with a). a can be pretty large and I want to avoid to do the selection multiple times.
Questions
How do I properly pass refa to the functions? What I want is bar(refa) and foobar(refa) to work.
Is there a better way to solve the problem, without changing the functions (too much)?
Code
#include <functional>
#include <iostream>
#include <vector>
int foo(int &a)
{
a++;
return 0;
}
int bar(std::vector<int> &va)
{
for(auto &vaa : va)
vaa++;
return 0;
}
int foobar(std::vector<int> va)
{
for(auto &vaa : va)
vaa++;
return 0;
}
int main()
{
std::vector<int> a= {1, 2, 3, 4, 5};
std::vector<std::reference_wrapper<int> > refa;
//Fill refa
for(auto &aa : a)
{
if(aa>1 && aa<4) refa.push_back(std::ref(aa));
}
//works
// for(auto &aa : refa)
// aa++;
//works
// bar(a);
//works, a unchanged
// foobar(a);
//works
// for(auto &aa : refa)
// foo(aa);
//works
// for(int &aa : refa)
// foo(aa)
// does not work
// conversion from vector<reference_wrapper<int> > to vector<int>& or vector<int> required
bar(refa);
// foobar(refa);
for(auto &aa : a)
std::cout << aa << std::endl;
return 0;
}
Note
int is only used here to keep the example simple.
I would definitely use iterators especially considering your problem ahead (work on a subset of a vector):
template<class Iterator>
int bar(Iterator begin, Iterator end)
{
for (auto it = begin; it != end; ++it)
(*it)++;
return 0;
}
So that not only you abstract away from the container, but you can also easily pass different iterators from the classical "begin" and "end" iterator to simulate specific ranges:
bar(a.begin() + 2, a.begin() + 4);
For example, with the above code you will visit elements from 1 to 4 (both excluded). And here's the live example.
Your best bet is to make the functions bar and foobar template functions, like this:
template <typename TContainer>
int bar(TContainer& va)
{
for(auto& vaa : va)
vaa++;
return 0;
}
Without redefining your function to accept "types that look like vector", I don't think that there's a way of achieving what you want.
If you don't need a container, don't use a container. Use an iterable range.
A container is both an iterable range, and an owner of its contents.
In the case of a vector, it is a contiguous iterable range and an owner of the contents. Odds are you only need to know if is a random access iterable range in your implementation code.
However, dealing with arbitrary contiguous iterable ranges has a cost, in that you have to put your implementation in a template header file, or do expensive type erasure. Two ways to approach this is to use the fact that you are only accepting sub-ranges of a vector, or use the fact that you are only accepting sub-ranges of a contiguous range.
I like the contiguous range idea myself:
template<typename T>
struct contiguous_range {
T* b; T* e;
contiguous_range( contiguous_range const& ) = default;
contiguous_range():b(nullptr), e(nullptr) {};
contiguous_range& operator=( contiguous_range const& ) = default;
std::size_t size() const { return e-b; }
T* begin() const { return b; } // note, T*
T* end() const { return e; } // note, T*
template<typename U, typename=typename std::enable_if<
sizeof(U)==sizeof(T)
&& std::is_convertible<U*, T*>::value
>::type>
contiguous_range( contiguous_range<U> const& o ):b(o.b), e(o.e) {};
T& operator[]( std::size_t i ) const { return b[i]; }
template<typename A>
contiguous_range( std::vector<T, A> const& v ):b(v.data()), e(v.data()+v.size()) {}
template<typename U, std::size_t N, typename=typename std::enable_if<
sizeof(U)==sizeof(T)
&& std::is_convertible<U*, T*>::value
>::type>
contiguous_range( std::array<U, N> const& a ):b(a.data()), e(a.data()+a.size()) {}
template<typename U, std::size_t N, typename=typename std::enable_if<
sizeof(U)==sizeof(T)
&& std::is_convertible<U*, T*>::value
>::type>
contiguous_range( U(&a)[N] ):b(&a[0]), e((&a[0])+N) {}
template<typename U, typename=typename std::enable_if<
sizeof(U)==sizeof(T)
&& std::is_convertible<U*, T*>::value
>::type>
contiguous_range( U* b_, U* e_ ):b(b_), e(e_) {}
};
template<typename I>
auto contiguous_subrange( I b, I e )
-> contiguous_range<std::iterator_traits<I>::value_type>
{
return {&*b, &*e};
}
template<typename C>
auto contiguous_subrange( C&& c, std::size_t start, std::size_t end )
-> decltype( contiguous_subrange( &c[start], &c[end] ) )
{ return ( contiguous_subrange( &c[start], &c[end] ) ) };
Now, our functions can simply take a contiguous_range<int> or continguos_range<const int>, and they can be implicitly passed a std::vector<int>.
You can also set up subranges of your std::vector that are equally contiguous.
Note that a constiguous_range<int> corresponds to a std::vector<int>&, and a contiguous_range<const int> corresponds to a std::vector<int> const&.
Related
I have a class which contains some vectors (minimal code here):
class Foo
{
public:
Foo() = default;
private:
void DoStuff();
std::vector<int>& mBarInt;
std::vector<double> mBarDouble;
int do_something_with_int;
double do_something_with_double;
};
On my DoStuff() method I have:
void Foo::DoStuff()
{
for(auto &val : mBarInt)
{
// do something here...
do_something_with_int = val + ....
}
}
I would like to do something like this, accessing the two vectors on the same for, but (see below after code) ...
for(int i = 0 ; i < mBarInt.size() ; i++)
{
// do something here...
do_something_with_int = mBarInt[i] + ....
do_something_with_double = mBarDouble[i] + .... // I know mBarDouble has the same size has mBarInt
}
... keeping the same c++11 syntax with the: for(auto &val : mBarInt)
Something like this below:
void Foo::DoStuff()
{
for(auto &val1, auto val2 : mBarInt, mBarDouble) //?????<---- How can I do this?
{
// do something with val1 and val2, which I know that will point to different vectors, but on the same index.
}
}
template<class It>
struct indexing_iterator {
It it = {};
using value_type = It;
using reference = value_type;
It operator*() const { return it; }
indexing_iterator& operator++() { ++it; return *this; }
friend bool operator==( indexing_iterator const& lhs, indexing_iterator const& rhs ) {
return lhs.it == rhs.it;
}
friend bool operator!=( indexing_iterator const& lhs, indexing_iterator const& rhs ) {
return !(lhs==rhs);
}
};
template<class F, class It_in>
struct transform_iterator_t:indexing_iterator<It_in> {
F f = {};
using reference = decltype(std::declval<F&>()( *std::declval<It_in&>() ) );
using value_type = std::decay_t<reference>;
using pointer = value_type*;
reference operator*() { return f(*this->it); }
transform_iterator_t( F f_in, It_in it_in ):
indexing_iterator<It_in>{it_in},
f(f_in)
{}
};
template<class F, class It_in>
transform_iterator_t<F,It_in> transform_iterator(
F f, It_in it
) {
return {f, it};
}
indexing_iterator<std::size_t> index( std::size_t n ) {
return {n};
}
template<class It>
struct range_t {
It b,e;
It begin() const { return b; }
It end() const { return b; }
};
template<class It>
range_t<It> range( It b, It e ) { return {b,e}; }
auto range_upto( std::size_t n ) {
return range( index(0), index(n) );
}
template<class C>
auto to_range( C&& c ) {
using std::begin; using std::end;
return range( begin(c), end(c) );
}
template<class It, class F>
range_t< transform_iterator< F, It > >
transform_range( F f, range_t<It> range ) {
return {
transform_iterator( f, range.begin() ),
transform_iterator( f, range.end() )
};
}
template<class F>
auto generator_range( F f, std::size_t count ) {
return transform_range( f, range_upto(count) );
}
template<class C1, class C2>
auto zip_over( C1& c1, C2& c2 ) {
return generator_range(
[&](std::size_t i) {
return std::tie( c1[i], c2[i] );
},
(std::min)(c1.size(), c2.size())
);
}
then in C++17 we have
void Foo::DoStuff() {
for(auto&&[val1, val2] : zip_over(mBarInt, mBarDouble)) {
}
}
or you can manually unpack the tuple in C++14.
Seems like a lot of work.
There are implementations of zip and the like in boost and other libraries, including Rangev3.
Code not tested. Design should be sound.
There is probably a shorter way to do it more directly, but I find generator via transform and indexing, and zip via generator, easier to understand and write with fewer bugs.
An indexing_iterator takes an incrementable comparable object, and iterates over it. The It can be an integer-like or an interator.
A transform_iterator takes an iterator and a transformation, and returns the transformation on each element.
Neither are full iterators; I skipped the boilerplate. But they are enough for for(:) loops.
A range holds 2 iterators and exposes them to for(:).
An indexing_iterator<std::size_t> is otherwise known as a counting iterator.
A generator iterator is a transformation of a counting iterator. It takes a function that takes an index, and generates an element for that index.
We zip over two random access containers by generating a generator iterator that calls operator[] on both, then returns a tuple. Fancier zipping handles non-random access containers.
The zip of two containers then lets you iterate in parallel over two different containers.
I then use C++17 structured bindings in the for(:) statement to unpack them into two different variables. We could instead just manually unpack as the first 2 statements in our for loop.
You can use boost::make_iterator_range and boost::zip_iterator like this:
std::vector<int> ints{1,2,3,4,5};
std::vector<double> doubles{1,2,3,4,5.1};
int do_something_with_int{0};
double do_something_with_double{0};
for (const auto& ref :
boost::make_iterator_range(
boost::make_zip_iterator(boost::make_tuple(ints.cbegin(), doubles.cbegin())),
boost::make_zip_iterator(boost::make_tuple(ints.cend(), doubles.cend()))))
{
do_something_with_int += boost::get<0>(ref);
do_something_with_double+= boost::get<1>(ref);
}
Live example.
I have some shared memory populated by specialized hardware. It's declared as an array of structs, like:
struct port {
int data[10];
char port_id[8];
}
struct bus {
port ports[5];
char bus_id[8];
}
struct bus busses[10];
I'm (re)learning C++, and wanted to use C++11's ranged for loops to iterate over the data.
HOWEVER: That last dimension of the array (data[10]), I only care about the first 4 elements. Is there a way to take a slice of the data and use it in the for() statement?
Like
for (auto & b : busses) {
for (auto & p : bus.ports) {
for (auto & d : port.data[0 through 3]) {
store_the_address_of_d_for_use_elsewhere(d);
}
}
}
Is there a way to use a cast in the innermost for loop, so that it appears like there's only 4 elements? The address of the data is important because I'm going to refer directly to it later using pointers.
This is probably one of those times when a good old-fashioned for (int i = 0; i < 4; i++) is your best bet.
Don't overthink it, and don't try to use "new" features just for the sake of it, creating more complexity and more work in the process.
template<class T>
struct array_view {
T* b = 0;
T* e = 0;
T* begin() const { return b; }
T* end() const { return e; }
std::size_t size() const { return end()-begin(); }
T& front() const { return *begin(); }
T& back() const { return *(end()-1); }
// basic constructors:
array_view(T* s, T* f):b(s), e(f) {}
array_view(T* s, std::size_t N):array_view(s, s+N) {}
// default ctors: (no need for move)
array_view()=default;
array_view(array_view const&)=default;
array_view& operator=(array_view const&)=default;
// advanced constructors:
template<class U>
using is_compatible = std::integral_constant<bool,
std::is_same<U, T*>{} || std::is_same<U, T const*>{} ||
std::is_same<U, T volatile*>{} || std::is_same<U, T volatile const*>{}
>;
// this one consumes containers with a compatible .data():
template<class C,
typename std::enable_if<is_compatible< decltype(std::declval<C&>().data()) >{}, int>::type = 0
>
array_view( C&& c ): array_view( c.data(), c.size() ) {}
// this one consumes compatible arrays:
template<class U, std::size_t N,
typename std::enable_if<is_compatible< U* >{}, int>::type = 0
>
array_view( U(&arr)[N] ):
array_view( arr, N )
{}
// create a modified view:
array_view without_front( std::size_t N = 1 ) const {
return {begin()+(std::min)(size(), N), end()};
}
array_view without_back( std::size_t N = 1 ) const {
return {begin(), end()-(std::min)(size(), N)};
}
array_view only_front( std::size_t N = 1 ) const {
return {begin(), begin()+(std::min)(size(), N)};
}
array_view only_back( std::size_t N = 1 ) const {
return {end()-(std::min)(size(), N), end()};
}
};
Now some functions that let you easily create it:
template<class T, std::size_t N>
array_view<T> array_view_of( T(&arr)[N] ) {
return arr;
}
template<class C,
class Data = decltype( std::declval<C&>().data() ),
class T = typename std::remove_pointer<Data>::type
>
array_view<T> array_view_of( C&& c ) {
return std::forward<C>(c);
}
template<class T>
array_view<T> array_view_of( T* s, std::size_t N ) {
return {s, N};
}
template<class T>
array_view<T> array_view_of( T* s, T* e ) {
return {s, e};
}
and we are done the boilerplate part.
for (auto & b : bus) {
for (auto & p : bus.port) {
for (auto & d : array_view_of(bus.data).only_front(4)) {
store_the_address_of_d_for_use_elsewhere(d);
}
}
}
live example
Now I would only advocate this approach because array_view is shockingly useful in many different applications. Writing it just for this case is silly.
Note that the above array_view is a multiply-iterated class; I've written it here before. This one is, in my opinion, better than the previous ones, other than the annoying c++11-isms I had to use.
for (auto & d : reinterpret_cast<int (&)[4]>(p))
I'm trying to make a template function that resizes a nested vector in all it's dimensions.
Pretty much like this: resizing multidimensional vector , but for an arbitrary nr. of dims.
(I suppose) The function would (at least) accept a reference to the vector (or vector<vector<T>> or v<v<v<T>>>, etc.) and a vector with the desired sizes. I now also have in index in the sizes vector, but it's probably not needed.
So far, this is what I ended up with (could be completely wrong):
template<typename V> void resize(vector<V> & V1, vector<int32_t> t, int32_t index) {
int32_t current_size=t.at(index);
cout << "resize dim [" << index << "] to size " << current_size <<endl ;
++index;
if (index < t.size()) {
// for each element ??
// for( int i = 0 ; i < V1.size(); i++ ) { resize (V1.at(i), t, index); } // doesn't work
// for( auto const& e : V1 ) { resize (e, t, index); } // doesn't work
// resize( V1, t, index); // recursive call, works, but doesn't do anything
}
I'd like to avoid copies of V1 or any of it's content. I'm not sure if there is a way with an iterator or for loop that passes references. If not, there probably needs to be a forth input to keep the index of V1?
Btw., I'm skipping the first dim on purpose, it is already the correct size.
Any help appreciated.
You're probably looking for something like this (this is c++14-compliant solution, similar one would be a little bit more tricky for c++11 but still possible):
#include <vector>
#include <tuple>
#include <utility>
template <class NestedVectorElement, class Tuple>
void nested_resize(std::vector<std::vector<NestedVectorElement>> &v, Tuple &&t);
template <class VectorElement, class Tuple>
void nested_resize(std::vector<VectorElement> &v, Tuple &&t);
template <class Vector, class Tuple, size_t... Is>
void nested_resize_impl(Vector &v, Tuple &&t, std::index_sequence<Is...>) {
v.resize(std::get<0>(t));
for(auto &nv: v) {
nested_resize(nv, std::forward_as_tuple(std::get<Is + 1>(t)...));
}
}
template <class NestedVectorElement, class Tuple>
void nested_resize(std::vector<std::vector<NestedVectorElement>> &v, Tuple &&t) {
nested_resize_impl(v, t, std::make_index_sequence<std::tuple_size<Tuple>::value - 1>{});
}
template <class VectorElement, class Tuple>
void nested_resize(std::vector<VectorElement> &v, Tuple &&t) {
v.resize(std::get<0>(t));
}
int main() {
std::vector<std::vector<std::vector<int>>> matrix;
nested_resize(matrix, std::make_tuple(3, 2, 3));
matrix.at(2).at(1).at(2) = 0; // at gives you an access only if element exists else throws an exception
}
The real problem here is that each instance of the template needs to generate code for two possibilities: the last dimension of the multidimensional vector, and all other dimensions of the vector. And in the case of the latter, it is necessary to recurse over the following dimensions of the vector, which will lead to an obvious compilation error in the case of the former.
This requires specialization:
#include <vector>
#include <iostream>
template<typename V, typename iter_type>
class resize_dim {
public:
static void resize(V & V1,
iter_type b, iter_type e)
{
if (b == e)
return;
V1.resize(*b);
}
};
template<typename V, typename iter_type>
class resize_dim<std::vector<std::vector<V>>, iter_type> {
public:
static void resize(std::vector<std::vector<V>> & V1,
iter_type b, iter_type e)
{
if (b == e)
return;
V1.resize(*b);
++b;
for (typename std::vector<std::vector<V>>::iterator
vb=V1.begin(),
ve=V1.end(); vb != ve; ++vb)
resize_dim<std::vector<V>, iter_type>::resize(*vb, b, e);
}
};
template<typename V>
void resize(V &v, const std::vector<size_t> &dimensions)
{
resize_dim<V, std::vector<size_t>::const_iterator>
::resize(v, dimensions.begin(), dimensions.end());
}
int main()
{
std::vector<std::vector<std::vector<int>>> v;
std::vector<size_t> d;
d.push_back(3);
d.push_back(3);
d.push_back(3);
resize(v, d);
std::cout << "Ok" << std::endl;
return 0;
}
The sizing vector, giving the size of each dimension should match the number of dimensions in the vector. Extra sizes are ignored. Fewer sizes result only in the leading dimensions getting resized.
This works:
template<typename V> void resizer(V & V1, vector<int32_t> const & t, int32_t index) {}
template<typename V> void resizer(vector<V> & V1, vector<int32_t> const & t, int32_t index) {
int32_t current_size=t.at(index);
V1.resize(t.at(index) );
++index;
if (index < t.size() ) {
for( auto & e : V1 ) {
resizer (e , t, index);
}
}
}
But this is actually a bit better, since we're not needlessly iterating over the last dimension's elements:
template<typename V> void resizer(vector<V> & V1, vector<int32_t> const & t, int32_t index) {
int32_t current_size=t.at(index);
V1.resize(t.at(index) );
}
template<typename V> void resizer(vector<std::vector<V>> & V1, vector<int32_t> const & t, int32_t index) {
int32_t current_size=t.at(index);
V1.resize(t.at(index) );
++index;
if (index < t.size() ) {
for( auto & e : V1 ) {
resizer (e , t, index);
}
}
}
Some higher-level languages have this feature, and I'm attempting to implement in C++. I'm not sure if there's a library that already does this somewhere, but if there is, it would save me a lot of trouble. Anyway, here's what I'm trying to accomplish:
Let's say I have a vector of structs that have a double and an int as members, and that I have another vector of ints that represents the indices of the elements in the vector of structs that I want to keep.
typedef struct
{
double x;
int y;
}s;
std::vector<s> v;
std::vector<int> inds;
Is there a way to implement accessing the elements in the structure using the vector of indices in a manner similar to this, or has it been implemented elsewhere?
std::vector<double> dResult = v[inds].x;
std::vector<int> iResult = v[inds].y;
It would also be nice to be able to access all of the elements in this manner:
std::vector<double> d = v.x;
Are these things possible?
You cannot use that syntax with existing definitions of std::vector.
You cannot create a global operator overload function that provides that syntax since operator[]() can be overloaded only as a member function.
You can create a non-member function that provides the functionality but without using that syntax.
template <typename T1, typename T2>
std::vector<T2> getElements(std::vector<T1> const& vec,
std::vector<int> const& indices,
T2 (T1::*member))
{
std::vector<T2> ret;
for ( auto index : indices )
{
ret.push_back(vec[index].*member);
}
return ret;
}
and then use it as:
std::vector<s> v;
std::vector<int> inds;
std::vector<double> dResult = getElements(v, inds, &s::x);
No such built-in functionality exists, and I'm not aware of any existing library solutions either. But it's not too difficult to write a couple of function templates that do this for you.
template<typename Container, typename T, typename M>
std::vector<M> select_mem(Container const& c, M T::* mem)
{
std::vector<M> result;
result.reserve(c.size());
std::transform(c.begin(), c.end(), std::back_inserter(result),
std::mem_fn(mem));
return result;
}
The above template takes a reference to a container and a pointer to a data member. It then copies that data member from each element in the input container into the output vector.
template<typename Container, typename Indices, typename T, typename M>
std::vector<M> select_mem(Container const& c, Indices const& ind, M T::* mem)
{
std::vector<M> result;
result.reserve(ind.size());
std::transform(ind.begin(), ind.end(), std::back_inserter(result),
[&c, mem](typename Indices::value_type const& i) {
return std::mem_fn(mem)(c[i]);
});
return result;
}
This is an extension of the previous template that also accepts a container of indices, and only copies the data members at the indicated indices within the input container.
Use them as follows:
std::vector<s> v {{10, 1}, {20, 2}, {30, 3}, {40, 4}};
for(auto const& x : select_mem(v, &s::x)) {
std::cout << x << ' ';
}
std::cout << '\n';
std::vector<int> indices{1,2};
for(auto const& x : select_mem(v, indices, &s::x)) {
std::cout << x << ' ';
}
std::cout << '\n';
Live demo
To get syntax close to what you desired you could create a class that wraps a vector of your structs and has member functions for each of the member variables in your struct:
class VecS {
const std::vector<s>& v;
const std::vector<int>* inds;
template<typename R>
std::vector<R> impl(R s::* pm) const {
if (inds) {
std::vector<R> ret(inds->size());
auto get_at_index = [this, pm](int index){ return v[index].*pm; };
std::transform(inds->begin(), inds->end(), ret.begin(), get_at_index);
return ret;
}
std::vector<R> ret(v.size());
std::transform(v.begin(), v.end(), ret.begin(), std::mem_fn(pm));
return ret;
}
public:
VecS(const std::vector<s>& v) : v(v), inds(nullptr) {}
VecS(const std::vector<s>& v, const std::vector<int>& inds) : v(v), inds(&inds) {}
std::vector<double> x() const { return impl(&s::x); }
std::vector<int> y() const { return impl(&s::y); }
};
If you are willing to abuse operator[] you can go one step further and add something like:
VecS operator[](const std::vector<int>& inds) { return VecS(v, inds); }
And then you can write:
auto vs = VecS(v);
auto dResult = vs[inds].x();
auto iResult = vs[inds].y();
and of course:
auto d = vs.x();
Live demo.
I made an N-dimensional structure with vectors and templates:
//----------------N-dimensional vector--------------------------------
template<int dim,typename T> class n_dim_vector {
public:
typedef std::vector<typename n_dim_vector<dim - 1, T>::vector> vector;
};
template<typename T> class n_dim_vector <0, T> {
public:
typedef T vector;
};
It can be instatiaated with different dimnsion-counts and is prt of a class that represent a search space.
template<int dim, typename T> class n_dim_ssc {
private:
typename n_dim_vector<dim, T>::vector searchspace;
};
My problem: I cannot get operator[] right to access searchspace properly, specifically the return type.
I tried:
template<typename V> std::vector<V>& operator[](unsigned i) {
return searchspace[i];
}
T& operator[](unsigned i) {
return searchspace[i];
}
at first, thinking the compiler would derive typename V as whatever type searchspace contained at all but the last level. Thats what T& operator[](unsigned i) was for.
But alas, doen't work this way. And I cannot work out how it would
EDIT Don't fear, I do not access empty memory, the structure is initialized and filled, I just didn't include the code for clarity's sake.
Also, I don't intend to access it with a single integer, I wanted to use searchspace[i][j]..[k]
The way to let compiler deduces the return type is auto:
In C++14:
auto operator[](unsigned i) { return searchspace[i]; }
In C++11:
auto operator[](unsigned i) -> decltype(searchspace[i]) { return searchspace[i]; }
I'm answering to your comment
Feel free to recommend something better, I'd appreciate it.
The following code shows one way to handle the multidimensional vector at once, i.e. non-recursively. It could be improved in several ways which I didn't consider for now (for instance, I wouldn't want to use and pass that many arrays but rather use variadic parameter lists. This however requires much more and more diffcult code, so I'll let it be.)
#include <numeric>
template<size_t Dim, typename T>
struct MultiDimVector
{
std::array<size_t, Dim> Ndim;
std::array<size_t, Dim> stride;
std::vector<T> container;
MultiDimVector(std::array<size_t, Dim> const& _Ndim) : Ndim(_Ndim), container(size())
{
stride[0] = 1;
for (size_t i = 1; i<Dim; ++i)
{
stride[i] = stride[i - 1] * Ndim[i - 1];
}
}
size_t size() const
{
return std::accumulate(Ndim.begin(), Ndim.end(), 1, std::multiplies<size_t>());
}
size_t get_index(std::array<size_t, Dim> const& indices) const
{
//here one could also use some STL algorithm ...
size_t ret = 0;
for (size_t i = 0; i<Dim; ++i)
{
ret += stride[i] * indices[i];
}
return ret;
}
T const& operator()(std::array<size_t, Dim> const& indices) const
{
return container[get_index(indices)];
}
};
You can use it like
MultiDimVector<3, double> v({ 3, 2, 5 }); //initialize vector of dimension 3x2x5
auto a = v({0,1,0}); //get element 0,1,0
But as I wrote, the curly brackets suck, so I'd rewrite the whole thing using variadic templates.
The problem with your approach is that you're not initializing any memory inside the vector and just trying to return non-existent memory spots. Something on the line of the following (WARNING: uncleaned and unrefactored code ahead):
#include <iostream>
#include <vector>
template<int dim,typename T> class n_dim_vector {
public:
typedef std::vector<typename n_dim_vector<dim - 1, T>::vector> vector;
};
template<typename T> class n_dim_vector <0, T> {
public:
typedef T vector;
};
template<int dim, typename T> class n_dim_ssc {
public:
typename n_dim_vector<dim, T>::vector searchspace;
n_dim_ssc() {}
n_dim_ssc(typename n_dim_vector<dim, T>::vector space) : searchspace(space) {}
n_dim_ssc<dim-1, T> operator[](std::size_t i) {
if(searchspace.size() < ++i)
searchspace.resize(i);
return n_dim_ssc<dim-1, T>(searchspace[--i]);
}
typename n_dim_vector<dim, T>::vector get() {
return searchspace;
}
};
template<typename T> class n_dim_ssc<0,T> {
public:
typename n_dim_vector<0, T>::vector searchspace;
n_dim_ssc() {}
n_dim_ssc(typename n_dim_vector<0, T>::vector space) : searchspace(space) {}
typename n_dim_vector<0, T>::vector get() {
return searchspace;
}
};
int main(int argc, char** argv) {
n_dim_ssc<0, int> ea;
int a = ea.get();
n_dim_ssc<1, int> ea2;
auto dd2 = ea2[0].get();
n_dim_ssc<2, int> ea3;
auto dd3 = ea3[0][0].get();
}
Try it out
will work with an accessor method (you can modify this as you want).
Anyway I strongly have to agree with Kerrek: a contiguous memory space accessed in a multi-dimensional array fashion will both prove to be faster and definitely more maintainable/easier to use and read.