I'm currently writing some utility functions for a templated container class for which the iterator seems to be working just fine. For example, the following code compiles and works as expected: (Class name Mat, containing type T)
void scalarFill(T x){
for(auto& i : *this){
i = x;
}
}`
However, when I call the range-based for loop on an a newly instantiated container, I get "error: no match for 'operator*' (operand type is 'MatIter')" such as in the following code:
static Mat zeros(size_t a){
Mat<double> result(a);
for(auto& i: result){
i = 0;
}
return result;
}
I've explicitly defined the iterator for my class, and there is certainly an 'operator*' function. Is there something I'm missing about how range-based for loops work? The following is a stripped down version of the full program which reproduces the error:
#include <stdlib.h>
#include <stdio.h>
using namespace std;
template <class T>
class MatIter;
template <class T = double>
class Mat {
friend class MatIter<T>;
public:
size_t columns = 0;
T* data;
MatIter<T> begin(){
return MatIter<T>(*this, 0);
}
MatIter<T> end(){
return MatIter<T>(*this, columns);
}
Mat(size_t a){
columns = a;
data = new T[a];
}
~Mat(){
delete []data;
}
//This function compiles and works as expected
void scalarFill(T x){
for(auto& i : *this){
i = x;
}
}
//this function throws "error: no match for 'operator*' (operand type is 'Matiter<double>')"
static Mat zeros(size_t a){
Mat<double> result(a);
for(auto& i: result){
i = 0;
}
return result;
}
};
template <class T>
class MatIter{
public:
Mat<T>& matrix;
size_t position;
MatIter(Mat<T>& mat, size_t pos) : matrix(mat), position(pos){}
bool operator==(MatIter b){
if(position == b.position) return true;
else return false;
}
bool operator!=(MatIter b){
if(position != b.position) return true;
else return false;
}
MatIter& operator++(){
position++;
return *this;
}
MatIter operator++(int){
MatIter<T> clone(*this);
position++;
return clone;
}
T & operator*(){
return matrix.data[position];
}
};
int main(){
Mat<> test(7);
test.scalarFill(5);
for(size_t i = 0; i < test.columns-1; i++){
printf("%g, ", test.data[i]);
}
printf("%g\n", test.data[test.columns-1]);
test = Mat<double>::zeros(7);
for(size_t i = 0; i < test.columns-1; i++){
printf("%g, ", test.data[i]);
}
printf("%g\n", test.data[test.columns-1]);
return 0;
}
Any insights would be greatly appreciated!
This fails because at the point where the compiler sees this loop, it has not yet seen a definition for the MatIter
template; MatIter<double>
is therefore an incomplete type, so the compiler doesn't know about the operator*
member function yet.
However, the result
variable shouldn't even be Mat<double>
here, it should just be Mat
(which is the same as Mat<T>
). Changing the type of result
to Mat
solves the problem. It forces the compiler to wait to figure out if the body is valid until it knows what T
is, and by then MatIter
has a definition, and the compiler can find the operator*
function.
Then you have a double-free problem because your Mat
template violates the rule of five -- the implicit copy and move constructor/assignment operations cause dual ownership of the data
pointer. The object returned out of Mat::zeros()
is used as the source of move-assignment to test
in main()
. This copies the data
pointer to test
but then the returned temporary is destroyed, which causes the data
pointer to be deleted, freeing the allocated memory. Then test
is destroyed, and the same pointer value is deleted a second time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With