Reputation: 51
I am practicing implementing a random access iterator for a custom class:
template<typename T>
class Array{
friend class Iterator;
private:
std::unique_ptr<T[]> data;
int front_index;
//other variables
public:
class Iterator{
friend class Array<T>;
private:
int position;
public:
Iterator(int pos) {position = pos; }
//some other iterator functions
template<typename T>
T operator*(){
return data[position];
}
};
Iterator begin() const { return Iterator(front_index); }
//other Array functions(insert, delete)
};
But when I call this code:
Array<int> a;
//insert some elements
Array<int>::Iterator iter1 = a.begin();
cout << "iterator is " << *iter1 << endl;
It gives the following error:
C++ no operator matches these operands, operand types are: * Array<int>::Iterator
It seems like the error is coming from the return data[position];
line. If I only write
return position
that the code runs but this is not what I want (I want that when I dereference the ierator returns the element in the specific position. Any inputs are appreciated!
Upvotes: 0
Views: 111
Reputation: 238401
It gives the following error:
C++ no operator matches these operands, operand types are: * Array<int>::Iterator
It seems like the error is coming from the return data[position]; line. If I only write return position that the code runs but this is not what I want (I want that when I dereference the ierator returns the element in the specific position.
It shouldn't, since that does nothing to solve this bug. It's unclear why you would think otherwise.
Your example has three fundamental problems. One is that you have nested template, where the inner template parameter shadows outer template parameter. This is not allowed in C++, resulting in the following error:
error: declaration of template parameter 'T' shadows template parameter
Simple solution is to use another name for any nested template.
Another problem is the one that you quote. Reading the error message further leads us to the solution:
error: no match for 'operator*' (operand type is 'Array<int>::Iterator')
35 | cout << "iterator is " << *iter1 << endl;
| ^~~~~~
<source>:19:15: note: candidate: 'template<class T> T Array<T>::Iterator::operator*() [with T = T; T = int]'
19 | T operator*(){
| ^~~~~~~~
<source>:19:15: note: template argument deduction/substitution failed:
<source>:35:32: note: couldn't deduce template parameter 'T'
35 | cout << "iterator is " << *iter1 << endl;
The compiler cannot know what template parameter to use, since it cannot be deduced from arguments, and you didn't specify any. Technically, this could be solved by specifying the argument explicitly:
<< iter1.operator*<int>()
However, I question why is the indirection operator a template in the first place? Why not always return T
of the outer template? I propose to make it a regular function:
// not a template
T // this is the outer template parameter
operator*(){
The third problem can be seen after these fixes:
error: invalid use of non-static data member 'Array<int>::data'
19 | return data[position];
This is the bug that your suggested change "solves" - in the sense that the program becomes well-formed. It just doesn't do anything meaninful.
The problem of course is that the iterator doesn't have a member data
. A typical solution is to store a non-owning pointer to element of the array, rather than just a position which cannot by itself be used to find elements of an array:
T* data;
T operator*(){
return *data;
In fact since pointer is an iterator for array, if you were lazy, instead of defining a custom class you could simply do:
using Iterator = int*;
That said, a custom class lets you catch some bugs at compile time, so it's a good design.
Upvotes: 4