Is there any way to overload the array subscript operator in C++ other than in a class? I would like to call a user-defined function when reading/writing in the array.
For example:
int* array = new int[10];
array[0] = 5;
When writing in array[0], I would like to call my own function. I know it can be done inside a class by overloading the operator[] (for example, a SafeArray class).
Thanks.
What you want is a wrapper class, something like:
template<typename T>
struct SubscriptWrapper {
SubscriptWrapper(T* array) : array_(array) {};
T& operator[](size_t index) {
// your magic goes in here
};
T* array_;
}
Related
I want to create a smaller vector class for a microcontroller.
In the normal vector class, your can do something like:
myvector[1] = 100;
How is it possible to achieve such an assignment in a class?
I tried this:
template<typename T>
class Vector
{
private:
T* content;
public:
T* operator[](unsigned int);
};
template <typename T>
T* Vector::operator[](unsigned int i)
{
return &content[i];
}
But, that throws errors, and it would also not be a nice solution.
So what should I do?
In the case you display above, you are returning a pointer to the value, which is presumably why you are having difficulties. Consider returning a reference instead:
T& operator[](unsigned int);
I use a C library which operates on 2D arrays in the form of Foo**. I use it inside C++ code, so I need some sort of wrapper. With 1D arrays it's straightforward because vector iterators are just pointers, but in case of 2D it gets more complicated. Is it possible to make a wrapper for Foo** without copying the data?
The elements of a vector<Foo> are stored in a dynamically allocated contiguous memory, so you can get a pointer Foo* to the array, as you do in your first case.
But the elements of a nested vector vector<vector<Foo> > are not stored as a contiguous 2D array, so you can't get a Foo** directly.
You could try something like this :
vector<vector<Foo> > data;
vector<Foo*> data_rows;
for(auto it = data.begin(); it != data.end(); ++it) {
//in c++11, you can use data() instead of casting begin()
data_rows.push_back(it->data());
}
Foo** c_data = data_rows.data();
That way you're not copying the data, just the row pointers.
I would propose to build a class overriding operator [], which holds the C pointer Foo** internally.
E.g.:
template <class T>
class Mat<T> {
private: T** ptr; int n; int m; //< 2D array is of size n x m
public: Mat( int n, int m ) : n(n), m(m) {}
Col<T> operator[]( int k ) { assert(k<n); return Col<T>(*(ptr+k*n)); }
T& get(int k, int i ) { return *(*(ptr+k*n)+i); }
}
having defined
template <class T>
class Col<T> { private: T* ptr;
public: T& operator[]( int i ) { return *(ptr+i); }
Col<T>(T* ptr) : ptr(ptr) { }
}
The code may not be 100% correct, but i hope you get the point and can reimplement it.
Also make sure livetime of pointerdata exceeds your c++ wrapper (also wrap the refcount mechanism of the c library?).
The nice point of operator[] is, that now you can use it like that:
Foo** ptr = from_some_c_library();
Mat<Foo> mat(ptr,3,4);
Foo& element_at_2_2 = mat[2][2];
assert( mat.get(2,2) == mat[2][2] );
Note that you may want to implement custom iterators for Mat<T> to make it work with STL functions.
I'm trying to learn more about templates and have come across a problem I can't seem to solve. At the moment the class below works fine.
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
template <class T, int s>
class myArray{
public:
T* data;
inline T& operator[](const int i){return data[i];}
myArray(){
data=new T[s];
}
myArray(const myArray& other){
data=new T[s];
copy(other.data,other.data+s,data);
}
myArray& operator=(const myArray& other){
data=new T[s];
copy(other.data,other.data+s,data);
return *this;
}
~myArray(){delete [] data;}
};
If I use it:
myArray<myArray<myArray<int,10>,20>,30> a;
a is now 30x20x10 array that I can access with the normal array brackets e.g. a[5][5][5]. I wish to add a feature so that I could write:
myArray<myArray<myArray<int,10>,20>,30> a(10);
and initialise all of the entries to 10 for example. I can't work out how to do this. As I understand, each layer of myArray is constructed using the default constructor. If I changed this to something like:
myArray(int n=0){
data=new T[s];
fill(data,data+s,n); //T might not be of type int so this could fail.
}
I think this should fail when data is not of type int (i.e. on any array on dimensions > 1), however it doesn't. It works when the array is square, but if not then some of the entries aren't set to 10. Does anyone have an idea how the standard vectors class achieves this? Any help would be amazing. Thanks!
Well, try something like this:
myArray()
: data(new T[s]()) // value-initialization!
{
}
myArray(T const & val)
: data(new T[s]) // default-initialization suffices
{
std::fill(data, data + s, val);
}
If you're into variadic templates, you might cook up something even more grotesque involving variadically filled initializer lists, but I think we've done enough learning for one week.
Note the fundamental flaw in using new: Either version requires that your class T can be instantiated in some "default" state, and that it be assignable, even though we never require the default state in the second version. That's why "real" libraries separate memory allocation and object construction, and you never see a new expression unless its the placement version.
std::vector uses placement new on memory blocks. It constructs the data.after allocating the memory in a second line of code.
This technique would work for you as well. Be careful with placement new as it requires you to call destructors manually as well.
Here is a half-assed route without placement new:
template<typename U>
explicit MyArray( U const& constructFromAnythingElse )
{
AllocateSpace(N); // write this, just allocates space
for (int i = 0; i < N; ++i)
{
Element(i) = T( constructFromAnythingElse );
}
}
with placement new, you have to allocate the memory first, then construct in-place, and then remember to destroy each element at the end.
The above is half-assed compared to a placement new route, because we first construct each element, then build another one, and use operator= to overwrite it.
By making it a template constructor on an arbitrary type, we don't rely on multiple conversion to get multiple levels down into the array. The naive version (where you take a T const&) doesn't work because to construct an array of arrays of arrays of T, the outermost one expects an array of arrays of T as an argument, which expects an array of T as an argument, which expects a T -- there are too many levels of user defined construction going on there.
With the above template constructor, the array of array of array of T accepts any type as a constructor. As does the array of array of T, as does the array of T. Finally, the T is passed in whatever you constructed the outermost array of array of array of T, and if it doesn't like it, you get a compiler error message that is nearly completely unreadable.
Make specialization for arrays containing other arrays. To do this you need some common implementation class to be used in general and specialized MyArray:
Common implementation (I made some fixes for you - see !!! comments):
template <class T, int s>
class myArrayImpl {
public:
T* data;
T& operator[](int i){return data[i];} //!!! const before int not needed
const T& operator[](int i) const {return data[i];} //!!! was missing
myArrayImpl(){
data=new T[s]();
}
myArrayImpl(const myArrayImpl & other){
data=new T[s];
copy(other.data,other.data+s,data);
}
myArrayImpl& operator=(const myArrayImpl& other){
T* olddata = data; // !!! need to store old data
data=new T[s];
copy(other.data,other.data+s,data);
delete [] olddata; //!!! to delete it after copying
return *this;
}
~myArrayImpl(){delete [] data;}
};
Then make general implementation - note the definition of value_type and setAll:
template <class T, int s>
class myArray : private myArrayImpl<T,s> {
typedef myArrayImpl<T,s> Impl;
public:
using Impl::operator[];
myArray() : Impl() {}
typedef T value_type; // !!!
explicit myArray(const value_type& value) {
setAll(value);
}
void setAll(const value_type& value) {
fill(this->data, this->data + s, value);
}
};
And the specialized version for myArray of myArray - see also differences in value_type and setAll:
template <class T, int s1, int s2>
class myArray<myArray<T,s2>,s1> : private myArrayImpl<myArray<T,s2>,s1> {
typedef myArrayImpl<myArray<T,s2>,s1> Impl;
public:
using Impl::operator[];
myArray() : Impl() {}
typedef typename myArray<T,s2>::value_type value_type; // !!!
explicit myArray(const value_type& value) {
setAll(value);
}
void setAll(const value_type& value) {
for_each(this->data, this->data + s1, [value](myArray<T,s2>& v) { v.setAll(value); });
}
};
And usage:
int main() {
myArray<myArray<myArray<int,7>,8>,9> a(7);
std::cout << a[0][0][0] << std::endl;
std::cout << a[8][7][6] << std::endl;
}
Full example here: http://ideone.com/0wdT9D
it is possible to overload somehow operator for multidimensional array?
Something like:
class A {
...
int& operator[][] (const int x, const int y);
...
}
Nope, that is not possible. There are two alternatives, though:
You can have operator[] return an array of a smaller dimension (For a 3D array, it will return a 2D array, for a 2D array it will return a 1D array, and for a 1D array, it will return a single element). Then you can "string them together" with the syntax you want. (arr[x][y][z])
Alternatively, you can overload operator(), because that can take multiple arguments.
Then you can use it like this, to index into a 3D array for example: arr(x,y,z)
But you can't overload [][] or [][][] as a single operator.
Not directly, but you can achieve the same functionality overloading operator[]() and having it return something that supports operator[]() itself.
For example:
class A {
std::vector<std::vector<int> > vec;
public:
std::vector<int>& operator[] (int x)
{
return vec[x];
}
};
would allow you to write:
A a;
//...
int y = a[1][2];
because a[1] returns a std::vector<int> to which you can apply operator[](2).
You need to overload operator[] and make it return a new class which only has another operator[].
No, there's just operator[]. As an alternative, you can overload:
int &operator()(int x, int y);
You can use that:
m(4, 5);
Since C++23, an overloaded operator[] will now take 0 or more arguments which behaves exactly the same as operator()
class A {
// ...
int& operator[](std::size_t x, std::size_t j);
// ...
};
invocation:
A a = /* ... */;
a[1, 2]; // equivalent to 'a.operator[](1, 2)'
It is a single operator being used twice to dereference. You can dereference [] operator and perform the functionality and usage by using it as [][] by changing the return type.
There's no operator like that. I implemented, some times ago, a matrix trying to be close to the stl standards.
And I used this method: first I've overloaded the operator[] to return another class that I called _C_row:
_C_row operator[](size_type index) { return _C_row(/*some parameters*/); } ///< This operator is overloaded to permit the use of double pointer notation.
_C_row operator[](size_type index) const { return _C_row(/*some parameters*/); } ///< This operator is overloaded to permit the use of double pointer notation.
And in _C_row I overloaded more than the operator[]:
value_type operator*() { return _r[0]; }
pointer operator->() { return _i[_idx]; }
double_pointer operator&() { return &(_i[_idx]); }
reference operator[](size_type col) { return _r[col]; }
const_reference operator[](size_type col) const { return _r[col]; }
I found this solution is very flexible. I hope my answer could be useful for you.
As mentionned before, there is no such thing as operator[][].
However, here is an an implementation using nested classes similar to what "jalf" proposed. For sake of simplicity, I hardcoded a 3x3 raw array.
class Array2D final{
public:
class PartialArr final{
private:
friend Array2D;
PartialArr(Array2D* ptr, int index) :original(ptr), firstIndex(index) {}
int firstIndex;
Array2D* original;
public:
int& operator[](int index) { return this->original->data[firstIndex][index]; }
};
PartialArr operator[](int index) { return PartialArr(this, index); }
private:
int data[3][3];
};
This solution prevents the user of Array2D to manipulate the data directly when indexing only the first dimension of the array.
const versions of both operator[] could also be added to make the class complete.
given the following template function :
template <class T>
void DoSomething(T &obj1, T &obj2)
{
if(obj1 > obj2)
cout<<"obj1 bigger: "<<obj1;
else if(obj1 == obj2)
cout<<"equal";
else cout<<"obj2 bigger: "<<obj2;
T tmp(3);
T array[2];
array[0]=obj1;
array[1]=obj2;
}
I need to define a class called MyClass (declarations only , i.e. just the .h file) , that would be able to work with that template function .
I defined the next declarations :
class MyClass
{
public:
MyClass(); // default ctor
MyClass(int x); // for ctor with one argument
bool operator ==(const MyClass& myclass) const;
bool operator >(const MyClass& myclass) const;
friend ostream& operator<<(ostream &out,const MyClass& myclass); // output operator
};
What I don't understand is why there is no need to define operator [] for the lines:
array[0]=obj1; array[1]=obj2;
? When would I need to define operator []?
thanks ,Ron
You declared an array for your type:
T array[2];
But your are talking about implementing operator[] for T, which is totally different concept.
If you need
T t;
t[1] = blah
Then you need to implement operator[]
Because
T array[2];
Isn't a T object, its an array of T. So
array[0];
Is indexing an array, not one of your objects, therefore you don't need an operator[].
Assuming you call DoSomething with a couple of MyClass objects, you have declared array to be a normal array of MyClass objects. You did not need a [] operator for MyClass because array is not an instance of MyClass; it is just an array.
You will want to overload the [] operator in your own classes when it makes sense to, or is convenient. A good example is a collection (such as a map). Another example is a custom string class, where you might want to index by a regex object to find matches for your pattern inside your string.
If your class was an implementation of a dynamic array, for example, you would want to access the (single) object as though it was an array - you this by overloading the [] operator.