Error C2676 binary '[': 'collection::item' does not define this operator or a conversion to a type acceptable to the predefined operator
I read few posts and Microsoft VS site, but cannot understand how to fix the problem
template<typename T1, typename T2>
class collection {
private:
class item {
public:
T1 item; T2 key;
};
unsigned int top;
item array = new item[top];
public:
collection& operator[](unsigned int i) {
return array[i];
}
collection(int top) {
this->top = top;
}
void coutarr() {
for (int i = 0; array[i] != 0; i++) {
cout << array[i].item << endl;
}
}
void extendarray() {
item x = new item[top*2];
for (int i = 0; i < top; i++) {
x[i] = array[i];
}
delete []array;
swap(array, x);
top = top*2;
}
void addvar(int i, T1 item, T2 key) {
array[i].item = item; array[i].key = key; //Here 2 Errors
}
};
If someone can explain what shall I do, I'd be very grateful. Thanks.
Error C2676 binary '[': 'collection::item' does not define this operator or a conversion to a type acceptable to the predefined operator
this is because
item array = new item[top];
must be
item * array = new item[top];
but you also have to move the initialization in the constructor when top has a value
Out of that is was better that the constructor collection(int top) get an unsigned value being collection(unsigned top) or better collection(size_t top)
Copy contructor, operator= ... are missing while an attribute is a pointer
What about to check the validity of i in collection& operator[](unsigned int i) and void addvar(int i, T1 item, T2 key) to produce an exception when it is invalid ? addvar is also a strange name because it is a set, a add means the size is increased. You cannot access to an element on a const instance, add const collection& operator[](unsigned int i) const
In void extendarray() to double the size will not do a lot if the initial size is 0 ;-)
Just a guess: top is unititialized. That makes array an array with zero length at best (which is really bad) and invokes undefined behavior at worst.
It seems you mean at least something like the following
template<typename T1, typename T2>
class collection {
private:
class item {
public:
T1 item; T2 key;
};
unsigned int top;
item *array;
^^^^^^^^^^^^^
public:
auto & operator[](unsigned int i) {
^^^^
return array[i];
}
collection(unsigned int top) : top( top ), array( new item[top]{} )
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}
~collection() { delete []array; }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
collection( const collection & ) = delete;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
collection & operator =( const collection & ) = delete;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
void coutarr() {
for (int i = 0; array[i] != 0; i++) {
cout << array[i].item << endl;
}
}
void extendarray() {
item x = new item[top*2];
for (int i = 0; i < top; i++) {
x[i] = array[i];
}
delete []array;
swap(array, x);
top = top*2;
}
void addvar(int i, const T1 &item, const T2 &key) {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
array[i].item = item; array[i].key = key; //Here 2 Errors
}
};
Of course you can futher develop the code.
Related
I am trying to implement a template Array class that can hold pointers as well as primitive data types.
But when calling the destructor, the pointers in that array are not properly deleting.
So I am trying to convert each item in that array to its corresponding class and call delete. But I met with an issue. I'm not able to convert T type to my class pointer.
My intention is to delete items in that array. Can someone please help in this?
template <class T>
class LIBRARY_EXPORT MyArrayT
{
public:
MyArrayT()
{
this->count = 0;
}
~MyArrayT()
{
if ((this->count) > 0)
{
//std::cout << "Deleting Array values" << std::endl;
delete[] this->values;
//free(this->values);
/*for (int i = 0; i < this->count; i++)
{
delete this->values[i];
}*/
this->count = 0;
}
}
void SetValue(std::vector< T > items)
{
//Delete existing memory
delete[] this->values;
this->count = items.size();
if (this->count > 0)
{
this->values = new T[this->count];
for (int i = 0; i < count; i++)
{
this->values[i] = items[i];
}
}
}
void SetValue(T items, int index)
{
if (this->count > index)
{
this->values[index] = items;
}
}
T GetValue(int index)
{
if (this->count > index)
{
return this->values[index];
}
return NULL;
}
T* GetValue()
{
return this->values;
}
_int64 GetCount()
{
return this->count;
}
private:
_int64 count;
T* values;
};
class LIBRARY_EXPORT MyString
{
public:
MyString();
~MyString();
void SetValue(std::string str);
std::string GetValue();
_int64 GetCount();
private:
_int64 count;
char* value;
};
int main()
{
MyArrayT<MyString*>* MyArrayValue = new MyArrayT<MyString*>() ;
vector<MyString*> value9;
MyString* opt1 = new MyString();
opt1->SetValue("Option: 1");
value9.push_back(opt1);
MyArrayValue->SetValue(value9);
MyArrayT<int>* MyArrayValueInt = new MyArrayT<int>() ;
vector<int> value1;
value1.push_back(1);
value1.push_back(2);
MyArrayValueInt->SetValue(value1);
delete MyArrayValue; //Calling this delete doesn't calling the ~MyString() destructor
delete MyArrayValueInt;
}
I created a Container class and used the new keyword in combination with pointers to learn how it works and how I can use it.
template<typename T>
class Container {
private:
T value;
public:
Container(T value) {
this->value = value;
}
Container() {
}
virtual ~Container() {
}
T getValue() {
return value;
}
void setValue(T value) {
this->value = value;
}
void operator[](T value) {
this->value = value;
}
};
int main() {
std::vector<Container<int>*> arr(10);
for (int i = 0; i < 10; i++) {
Container<int> *a = new Container<int>;
a->setValue(i);
// a[i];
arr[i] = a;
std::cout << arr[i]->getValue() << std::endl;
delete a;
}
return 0;
}
The [] operator has the same code as setValue(), but it only prints the numbers from 0 to 9 if I use a->setValue(i) and with using a[i] it prints just a random number. Why?
See Sly_TheKing's answer (applying index operator to pointer).
The index operator is intended to access values at a specific offset to some reference. It should accept a signed or unsigned integer value and return some specific value. So the operator, to be valid, should look like this:
T& operator[](size_t index)
{
return value;
}
Actually, as you do not have anything you could apply an index to (the only valid index in your case would be 0 – or from another point of view, with above implementation, any index would return the same value, so &a[0] == &a[1] would apply - which might be syntactically correct, but violates the semantics of an index operator...), dereferencing operators would be more appropriate:
T& operator*() { return value; }
T& operator->() { return value; }
Possibly, you could add an assignment operator, too (would replace setValue):
Container& operator=(T const& value) { this->value = value; return *this; }
In line
Container<int> *a = new Container<int>;
you initialize a as pointer, so with this line
a[i];
you just access some memory with address stored in a and offset i * sizeof(container<int>)
So the correct usage would be
std::vector<Container<int>*> arr(10);
for (int i = 0; i < 10; i++) {
Container<int> *a = new Container<int>;
(*a)[i];
arr[i] = a;
std::cout << arr[i]->getValue() << std::endl;
delete a;
}
With (*a)[i]; you access operator[] you wrote in your class
can somebody ewxplain me how the compiler calls the operator casting:
operator ELEMENT()const {
return pArray->arr[index];
}
From line 6 in main:
(*ptr3)[0] = a1[0] + a2[1];
How adding two ELEMENT object with + operator even allowed? There is no + operator overloading in ELEMENT class.
Thanks,
Liron
#include <iostream>
using namespace std;
template<class ELEMENT> class Array
{
class Element
{
Array<ELEMENT>* pArray;
int index;
public:
Element(Array<ELEMENT>* p, int i)
: pArray(p), index(i) {}
const Element& operator=(const ELEMENT& e) {
pArray->set(index, e); // call copy-on-write
return *this;
}
operator ELEMENT()const {
return pArray->arr[index];
}
};
friend class Element;
ELEMENT* arr;
int size;
int* ref_counter;
void attach(const Array& a) {
arr = a.arr; size = a.size;
ref_counter = a.ref_counter;
++(*ref_counter);
}
void detach() {
if(--(*ref_counter) == 0) {
delete []arr;
delete ref_counter;
}
}
void set(int index, const ELEMENT& e) {
if(*ref_counter > 1) { // need copy-on-write!
Array temp = clone();
detach();
attach(temp);
}
arr[index] = e;
}
public:
explicit Array(int);
Array<ELEMENT> clone()const;
Array(const Array<ELEMENT>& a){attach(a);}
~Array(){detach();}
const Array& operator=(const Array<ELEMENT>& a) {
detach(); attach(a); return *this;
}
Element operator[](int index) {
return Element(this, index);
}
const ELEMENT& operator[](int index)const {
return arr[index];
}
};
template<class ELEMENT>
Array<ELEMENT>::Array(int size1)
: size(size1), ref_counter(new int(1))
{
arr = new ELEMENT[size];
}
template<class ELEMENT>
Array<ELEMENT> Array<ELEMENT>::clone()const {
Array temp(size);
for(int i=0; i<size; ++i) {
temp.arr[i] = arr[i];
}
return temp;
}
int main()
{
Array<int> a1(1), a2(2);
Array<int>* ptr3 = new Array<int>(3);
a2[0] = 1;
a2[1] = 2;
a1 = a2;
(*ptr3)[0] = a1[0] + a2[1];
(*ptr3)[1] = a1[1] + a2[0];
cout << (*ptr3)[0] << ", " << (*ptr3)[1] << endl;
delete ptr3;
return 1;
}
How adding two ELEMENT object with + operator even allowed? There is no + operator overloading in ELEMENT class.
ELEMENT is not a class, it's a type parameter. And in your example the type given for that parameter is int. Obviously int does have a + operator, so that works fine. If you tried to create an Array<SomeType> where SomeType did not have a + operator, you would get an error.
There is an Element class and that class does indeed have no + operator, but that class is implicitly convertible to ELEMENT (i.e. int in this case), so when you apply + to objects of the Element the compiler adds a call to that conversion operator and + is applied to the result.
#include <iostream>
using namespace std;
template <typename T>
class DynamicArray
{
T *array;
unsigned int elements;
unsigned int size;
public:
void expand_array(int extra_size)
{
T *new_array= new T[size + extra_size];
//copy integers from old array
memcpy(new_array,array,elements*sizeof(T));
//make 0 the integers from new part of the array
memset(new_array + size, 0, extra_size * sizeof(T));
delete [] array;
array = new_array;
size += extra_size;
}
//constructors
DynamicArray()
{
array = new T[8];
elements = 0;
size = 8;
memset(array,0,size*sizeof(T));
}
DynamicArray(const unsigned int size)
{
array= new T[size];
}
virtual ~DynamicArray()
{
delete [] array;
}
void add(T new_element)
{
if(elements>=size)
expand_array(8);
array[elements++] = new_element;
}
int get (const unsigned int index) const
{
if(index< elements)
return array[index];
return -1;
}
void add(const unsigned int index, T new_element)
{
if(index>size)
expand_array(index- size +1);
array[index] = new_element;
elements = index +1;
}
DynamicArray &operator=(DynamicArray &ab)
{
elements=ab.elements;
size=ab.size;
if (this == &ab)
return *this;
delete array;
if(ab.array)
{
array= new T[size];
memcpy(array,ab.array,elements*sizeof(T));
}
else
{
array=0;
}
return *this;
}
DynamicArray(const DynamicArray& source)
{
elements=ab.elements;
size=ab.size;
if(ab.array)
{
array= new T[size];
memcpy(array,ab.array,elements*sizeof(T));
}
else
{
array=0;
}
}
};
int main()
{
DynamicArray<int> da(2);
DynamicArray<int> db(2);
DynamicArray< DynamicArray<int> > array_of_arrays(2);
array_of_arrays[0] = da;/
//array_of_arrays[1] = db;
/*
da[0]=5;
da[1]=2;
db[0]=3;
db[1]=4;
cout<<array_of_arrays[0][0]<<endl;
*/
system("color 0C");
system("pause");
return 0;
}
I need help in understanding why this gives me an error:
error C2676: binary '[' : 'DynamicArray<T>' does not define this operator or a conversion to a type acceptable to the predefined operator
i added this
T& operator[](unsigned int index)//important to provide this operator
{
if (index > size)
{
expand_array(index-size);
}
return array[index];
}
but subequently i got invalid allocation size error during runtime.I try to try it and figure that the problem is somewhere about this area
if(ab.array)
{
array= new T[size];
memcpy(array,ab.array,elements*sizeof(T));
}
but i have no idea why is that so.
Any ideas?
Thanks I figured it out: One of the attributes are not initialise to a proper valuein one of the constructors
DynamicArray(const unsigned int size)
.
Thanks for all the help.
The error has nothing to do with templates. The problem is that you use the [] operator on array_of_arrays. However the DynamicArray class does not define operator[].
What it does define is a method named add, so maybe you meant to call that instead? I.e. array_of_arrays.add(0, da); instead of array_of_arrays[0] = da;.
However it would probably be more idiomatic to simply define operator[] on your class.
DynamicArray does not define a subscript operator.
This would typically look like:
T& operator[] (unsigned int index)
{
if (index > size)
{
// Handle error
}
return array[index];
}
I have a technical problem and it's really confusing me. I apologise in advance because I may not be giving the relevant details; I don't yet why it's going wrong and it would be excessive to include all the code I'm working with.
I'm working with a large program that uses the C++ STL. I'm moving this code to a very sensitive environment without a standard clib nor STL implementaton; it will redefine malloc/free/new/delete etc... For that, I need to replace the std:: parts with my own simplified implementations. I've started with std::vector. Right now it's running in the standard ecosystem so it's the GNU libc and STL. The only thing that's changed is this vector class.
When I execute the program with the replaced class, it segfaults. I've put this through GDB and found that the program will request an object from the vector using the subscript operator. When the object reference is returned, a method is invoked and the program segfaults. It seems it can't find this method and ends up in main_arena() in GDB. The type of the object is an inherited class.
I'm really not sure at all what the problem is here. I would love to provide additional details, but I'm not sure what more I can give. I can only assume something is wrong with my vector implementation because nothing else in the program has been changed. Maybe there's something obvious that I'm doing wrong here that I'm not seeing at all.
I'm using: g++ (GCC) 4.4.5 20110214 (Red Hat 4.4.5-6)
I'd really appreciate any feedback/advice!
#ifndef _MYSTL_VECTOR_H_
#define _MYSTL_VECTOR_H_
#include <stdlib.h>
#include <assert.h>
typedef unsigned int uint;
namespace mystl
{
/******************
VECTOR
********************/
template <typename T>
class vector
{
private:
uint _size;
uint _reserved;
T *storage;
void init_vector(uint reserve)
{
if (reserve == 0)
{
_reserved = 0;
return;
}
storage = (T*)malloc(sizeof(T)*reserve);
assert(storage);
_reserved = reserve;
}
public:
vector()
{
// std::cerr << "default constructor " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
}
vector(const vector<T> &other)
{
// std::cerr << "copy constructor " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(other.size());
_size = other.size();
for (uint i=0; i<other.size(); i++)
{
storage[i] = T(other[i]);
}
}
vector(uint init_num, const T& init_value)
{
// std::cerr << "special constructor1 " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(init_num);
for (size_t i=0; i<init_num; i++)
{
push_back(init_value);
}
}
vector(uint init_num)
{
// std::cerr << "special constructor2 " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(init_num);
}
void reserve(uint new_size)
{
if (new_size > _reserved)
{
storage = (T*)realloc(storage, sizeof(T)*new_size);
assert(storage);
_reserved = new_size;
}
}
void push_back(const T &item)
{
if (_size >= _reserved)
{
if (_reserved == 0) _reserved=1;
reserve(_reserved*2);
}
storage[_size] = T(item);
_size++;
}
uint size() const
{
return _size;
}
~vector()
{
if (_reserved)
{
free(storage);
storage = NULL;
_reserved = 0;
_size = 0;
}
}
// this is for read only
const T& operator[] (unsigned i) const
{
// do bounds check...
if (i >= _size || i < 0)
{
assert(false);
}
return storage[i];
}
T& operator[] (unsigned i)
{
// do bounds check...
if (i >= _size || i < 0)
{
assert(false);
}
return storage[i];
}
// overload = operator
const vector<T>& operator= (const vector<T>& x)
{
// check for self
if (this != &x)
{
_reserved = 0;
_size = 0;
storage = NULL;
init_vector( x.size() );
for(uint i=0; i<x.size(); i++)
{
storage[i] = T(x[i]);
}
_size = x.size();
}
return *this;
}
uint begin() const
{
return 0;
}
void insert(uint pos, const T& value)
{
push_back(value);
if (size() == 1)
{
return;
}
for (size_t i=size()-2; i>=pos&& i>=0 ; i--)
{
storage[i+1] = storage[i];
}
storage[pos] = value;
}
void erase(uint erase_index)
{
if (erase_index >= _size)
{
return;
}
//scoot everyone down by one
for (uint i=erase_index; i<_size; i++)
{
storage[i] = storage[i+1];
}
_size--;
}
void erase(uint start, uint end)
{
if (start > end)
{
assert(false);
}
if (end > _size)
end = _size;
for (uint i=start; i<end; i++)
{
erase(start);
}
assert(false);
}
void clear()
{
erase(0,_size);
}
bool empty() const
{
return _size == 0;
}
}; //class vector
}
#endif // _MYSTL_VECTOR_H_
Wow!
Your assignment operator also leaks memory.
Becuause you are using malloc/release the constructor to your type T will will not be called and thus you can not use your vector for anything except the most trivial of objects.
Edit:
I am bit bored this morning: Try this
#include <stdlib.h> // For NULL
#include <new> // Because you need placement new
// Because you are avoiding std::
// An implementation of swap
template<typename T>
void swap(T& lhs,T& rhs)
{
T tmp = lhs;
lhs = rhs;
rhs = tmp;
}
template <typename T>
class vector
{
private:
unsigned int dataSize;
unsigned int reserved;
T* data;
public:
~vector()
{
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Because we use placement new we must explicitly destroy all members.
data[loop].~T();
}
free(data);
}
vector()
: dataSize(0)
, reserved(10)
, data(NULL)
{
reserve(reserved);
}
vector(const vector<T> &other)
: dataSize(0)
, reserved(other.dataSize)
, data(NULL)
{
reserve(reserved);
dataSize = reserved;
for(unsigned int loop;loop < dataSize;++loop)
{
// Because we are using malloc/free
// We need to use placement new to add items to the data
// This way they are constructed in place
new (&data[loop]) T(other.data[loop]);
}
}
vector(unsigned int init_num)
: dataSize(0)
, reserved(init_num)
, data(NULL)
{
reserve(reserved);
dataSize = reserved;
for(unsigned int loop;loop < dataSize;++loop)
{
// See above
new (&data[loop]) T();
}
}
const vector<T>& operator= (vector<T> x)
{
// use copy and swap idiom.
// Note the pass by value to initiate the copy
swap(dataSize, x.dataSize);
swap(reserved, x.rserved);
swap(data, x.data);
return *this;
}
void reserve(unsigned int new_size)
{
if (new_size < reserved)
{ return;
}
T* newData = (T*)malloc(sizeof(T) * new_size);
if (!newData)
{ throw int(2);
}
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Use placement new to copy the data
new (&newData[loop]) T(data[loop]);
}
swap(data, newData);
reserved = new_size;
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Call the destructor on old data before freeing the container.
// Remember we just did a swap.
newData[loop].~T();
}
free(newData);
}
void push_back(const T &item)
{
if (dataSize == reserved)
{
reserve(reserved * 2);
}
// Place the item in the container
new (&data[dataSize++]) T(item);
}
unsigned int size() const {return dataSize;}
bool empty() const {return dataSize == 0;}
// Operator[] should NOT check the value of i
// Add a method called at() that does check i
const T& operator[] (unsigned i) const {return data[i];}
T& operator[] (unsigned i) {return data[i];}
void insert(unsigned int pos, const T& value)
{
if (pos >= dataSize) { throw int(1);}
if (dataSize == reserved)
{
reserve(reserved * 2);
}
// Move the last item (which needs to be constructed correctly)
if (dataSize != 0)
{
new (&data[dataSize]) T(data[dataSize-1]);
}
for(unsigned int loop = dataSize - 1; loop > pos; --loop)
{
data[loop] = data[loop-1];
}
++dataSize;
// All items have been moved up.
// Put value in its place
data[pos] = value;
}
void clear() { erase(0, dataSize);}
void erase(unsigned int erase_index) { erase(erase_index,erase_index+1);}
void erase(unsigned int start, unsigned int end) /* end NOT inclusive so => [start, end) */
{
if (end > dataSize)
{ end = dataSize;
}
if (start > end)
{ start = end;
}
unsigned int dst = start;
unsigned int src = end;
for(;(src < dataSize) && (dst < end);++dst, ++src)
{
// Move Elements down;
data[dst] = data[src];
}
unsigned int count = start - end;
for(;count != 0; --count)
{
// Remove old Elements
--dataSize;
// Remember we need to manually call the destructor
data[dataSize].~T();
}
}
unsigned int begin() const {return 0;}
}; //class vector
With your current memory handling, this vector would only work with plain old data types.
To handle all types, it must ensure that objects
are actually created (malloc doesn't do that),
destroyed (free doesn't do that),
and you can't reallocate memory with realloc, because complex objects are not guaranteed to remain valid if they are byte-wise copied to another location.
Looks like the answer can be found in your question: "When the object reference is returned, a method is invoked and the program segfaults. It seems it can't find this method and ends up in main_arena() in GDB. The type of the object is an inherited class."
You probably store base class instance T in the vector, but make push_back for the instance of the class inherited from T. In push_back {storage[_size] = T(item);} you cast (actually make copy constructor T:T(const T&)) item to T (this probably named 'type cut'), then get reference to T and invoke a method of the class inherited from T using virtual table of T where the method is not defined yet/abstract. Am I right?
To make it properly work you should put T* in the vector or shared_ptr/unique_ptr depending on the ownership terms you apply to vector elements.
Generally in vector you can store only POD (Plain Old Data) types.