Vector find with pointers of custom class - c++

I am trying to understand operators you need to overload when working with custom classes in STL(SCL).
Can any one please tell me what is it I am doing wrong ?
class myClass
{
public:
int data;
myClass()
{
data =0;
cout<<"Default const "<<endl;
}
myClass(int x)
{
data = x;
cout<<"Int constructor"<<endl;
}
myClass(const myClass &m)
{
cout<<"Copy constructor"<<endl;
}
bool operator == (const myClass &temp)
{
cout<<"Operator called &";
return data == temp.data;
}
bool operator == (const myClass *temp)
{
cout<<"Operator called *";
return data == temp->data;
}
};
int main ()
{
/*
vector<int> myvector;
myvector.push_back(10);
myvector.push_back(20);
myvector.push_back(30);
cout << "myvector contains:";
for_each (myvector.begin(), myvector.end(), meObj);
*/
vector<myClass*> myVec;
myClass temp;
myVec.push_back(&temp);
myClass temp2(19);
myVec.push_back(&temp2);
myClass temp3(19);
vector<myClass*>::iterator it = find(myVec.begin(),myVec.end(),&temp2); //works
if(it!=myVec.end())
{
cout<<"Value is "<<(*it)->data;
}
vector<myClass*>::iterator dit = find(myVec.begin(),myVec.end(),&temp3); //fails
if(dit!=myVec.end())
{
cout<<"Value is "<<(*dit)->data;
}
cout << endl;
return 0;
}
Please correct me if I am wrong, but the first find works as it does a address comparison. What do I need to overload for the above to work ?
Do both the signature make sense ?
bool operator == (const myClass &temp); // seen in many places
bool operator == (const myClass *temp); // what if two pointer types of same object are being compared?
Cheers!

Operator overloads must have at least one user-defined type. So you cannot overload operator== for two pointers, for instance.
Your myClass::operator==(const myClass *temp) is valid in the sense that it compiles, but makes very little semantic sense, and is not recommended (there are very few situations where you'd want to do T x; T *y; ... (x == y)).
For your situation, where you have a vector of pointers, you may want to consider std::find_if, which takes a predicate. Something like:
class CompareByPointer
{
public:
explicit CompareByPointer(const myClass &p) : p(p) {}
bool operator() (const myClass &rhs) const { return p->data == rhs->data; }
private:
const myClass &p;
};
...
find_if(myVec.begin(), myVec.end(), CompareByPointer(&temp2));
[As a side note, you should generally define member functions const wherever possible. So your operator overloads should be const.]

In the sample code, you haven't pushed &temp3 into myVec. So it makes sense for the second std::find to fail.

What do you mean by "work" in this case? Generally, when you're storing pointers, it's because the objects do have identity, and comparing the address is the correct thing to do. Otherwise, you should probably be storing values (although there are exceptions). Anyway, you can always use find_if, and any comparison criteria you want. For anything but the simplest types, I find myself using find_if more often than find anyway; usually, you're not looking for equality, but rather for some specific type of match. Here, for example, you'd more likely want something like:
std::vector<MyClass>::iterator it = std::find_if( myVect.begin(), myVect.end(),
boost::bind(&MyClass::id, _1, 19) );
(Supposing that the data here is some sort of identifier, and that you've provided a member function, myClass::id() to read it.)

Related

Insert Objects into Vector when Class Has Constants

Say I have a class that has only constants, since the values should never change.
struct Test {
const std::string id;
const int a;
const double b;
};
Later on, I want to add objects into a vector, but I want the vector to be sorted by b from largest to smallest. I use insertion sort, since there will only ever be a small number (maybe 5).
std::vector<Test> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && itr->b > b ) ++itr;
values.insert( itr, { id, a, b } );
// end sort
}
Attempting the above, I get the following error while attempting to insert into the vector:
error: object of type 'Test' cannot be assigned because its copy assignment operator is implicitly deleted
I would like to keep with using a vector for this problem; but I can't seem to find a way around the sorting issue. The only other option I could think of was to effectively recreate the vector constantly. Alternatively, using a multi-set or similar, then once all values are added, I could dump into an array.
Is there a way around this limitation, still using a vector and not making everything non-const? Or will I be forced to change my structure, or move away into a temporary object first?
Edit: Also trying to avoid the use of pointers
As has already been pointed out in the comments, sorting requires moving around elements in your vector. And moving around elements generally requires move-assignment, which generally requires mutation of elements…
There is one way out of this dillema: instead of assigning a new value to the existing object, create a new object with the new value on top of the existing one. One can do so by defining a copy/move constructor like, e.g.:
Test& operator =(Test&& other)
{
this->~Test();
return *new (this) Test(std::move(other));
}
There is just one problem with this: via [basic.life]/8.3, we're not allowed to use any existing pointer or reference to the original object, or even just the name of the original object after such an assignment. You would always have to use the result of the assignment (the return value of placement-new) or a laundered pointer as the sole means of accessing the object going forward. Since it is not specified how std::sort operates exactly, we cannoy rely on it doing so.
What we could to is build a wrapper like this (noexcept ommited for readability):
template <typename T>
class const_element
{
T value;
const T& laundered_value() const { return *std::launder(&value); }
T& laundered_value() { return *std::launder(&value); }
public:
template <typename... Args>
explicit const_element(Args&&... args) : value { std::forward<Args>(args)... } {}
const_element(const const_element& e) : value(e.laundered_value()) {}
const_element(const_element&& e) : value(std::move(e.laundered_value())) {}
const_element& operator =(const const_element& e)
{
laundered_value().~T();
new (&value) T(e.laundered_value());
return *this;
}
const_element& operator =(const_element&& e)
{
laundered_value().~T();
new (&value) T(std::move(e.laundered_value()));
return *this;
}
~const_element()
{
laundered_value().~T();
}
operator const T&() const { return laundered_value(); }
operator T&() { return laundered_value(); }
friend bool operator <(const_element& a, const_element& b)
{
return a.laundered_value() < b.laundered_value();
}
};
What this does is wrap an object of some type T, implement copy and move assignment in the way described above, and make sure that any access to the current value always goes through a laundered pointer.
Then we can just do
std::vector<const_element<Test>> values;
values.emplace_back("b", 1, 0.0);
values.emplace_back("a", 0, 0.0);
values.emplace_back("c", 2, 0.0);
std::sort(begin(values), end(values));
working example here
All that being said, I would recommend to just not do this. If you want an object that cannot be modified, simply use a const T rather than a T that solely consists of const members. You cannot have a vector of const T. But you can have a vector of T and then just pass around a reference to a const vector or a range of const elements…
You could store pointers in the vector. Of course, you would also need to clean everything up. Example at http://cpp.sh/3h7dr
std::vector<Test*> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && ((*itr)->b > b) ) ++itr;
Test* t = new Test{id,a,b};
values.insert( itr, t );
// end sort
}

Square bracket [] operator overloading c++

I have a project that wants me to make a BigNum class in c++ (university project)
and it said to overload operator bracket for get and set
but the problem is if the set was invalid we should throw an exception the invalid is like
BigNum a;
a[i]=11;//it is invalid because its >9
in searching I found out how to make the set work
C++ : Overload bracket operators [] to get and set
but I didn't find out how to manage setting operation in c# you easily can manage the set value what is the equivalent of it in c++
to make it clear in C# we can say
public int this[int key]
{
set
{
if(value<0||value>9)throw new Exception();
SetValue(key,value);
}
}
New Answer
I have to rewrite my answer, my old answer is a disaster.
The check should happen during the assignment, when the right hand side (11) is available. So the operator which you need to overload is operator=. For overloading operator=, at least one of its operands must be an user defined type. In this case, the only choice is the left hand side.
The left hand side we have here is the expression a[i]. The type of this expression, a.k.a the return type of operator[], must be an user defined type, say BigNumberElement. Then we can declare an operator= for BigNumberElement and do the range check inside the body of operator=.
class BigNum {
public:
class BigNumberElement {
public:
BigNumberElement &operator=(int rhs) {
// TODO : range check
val_ = rhs;
return *this;
}
private:
int val_ = 0;
};
BigNumberElement &operator[](size_t index) {
return element_[index];
}
BigNumberElement element_[10];
};
OLD answer
You can define a wapper, say NumWapper, which wraps a reference of BigNum's element. The operator= of BigNum returns the wrapper by value.
a[i]=11;
is then something like NumWrapper x(...); x = 11. Now you can do those checks in the operator= of NumWrapper.
class BigNum {
public:
NumWrapper operator[](size_t index) {
return NumWrapper(array_[index]);
}
int operator[](size_t index) const {
return array_[index];
}
};
In the NumWrapper, overload some operators, such as:
class NumWrapper {
public:
NumWrapper(int &x) : ref_(x) {}
NumWrapper(const NumWrapper &other) : ref_(other.ref_) {}
NumWrapper &operator=(const NumWrapper &other);
int operator=(int x);
operator int();
private:
int &ref_;
};
You can also declare the NumWrapper's copy and move constructor as private, and make BigNum his friend, for preventing user code from copying your wrapper. Such code auto x = a[i] will not compile if you do so, while user code can still copy the wrapped value by auto x = static_cast<T>(a[i]) (kind of verbose though).
auto &x = a[i]; // not compiling
const auto &x = a[i]; // dangerous anyway, can't prevent.
Seems we are good.
These is also another approach: store the elements as a user defined class, say BigNumberElement. We now define the class BigNum as :
class BigNum {
// some code
private:
BigNumberElement array_[10];
}
We need to declare a whole set operators for BigNumberElement, such as comparison(can also be done through conversion), assignment, constructor etc. for making it easy to use.
auto x = a[i] will now get a copy of BigNumberElement, which is fine for most cases. Only assigning to it will sometimes throw an exception and introduce some run-time overhead. But we can still write auto x = static_cast<T>(a[i]) (still verbose though...). And as far as I can see, unexpected compile-time error messages is better than unexpected run-time exceptions.
We can also make BigNumberElement non-copyable/moveable... but then it would be the same as the first approach. (If any member functions returns BigNumberElement &, the unexpected run-time exceptions comes back.)
the following defines a type foo::setter which is returned from operator[] and overloads its operator= to assign a value, but throws if the value is not in the allowed range.
class foo
{
int data[10];
public:
void set(int index, int value)
{
if(value<0 || value>9)
throw std::runtime_error("foo::set(): value "+std::to_string(value)+" is not valid");
if(index<0 || index>9)
throw std::runtime_error("foo::set(): index "+std::to_string(index)+" is not valid");
data[index] = value;
}
struct setter {
foo &obj;
size_t index;
setter&operator=(int value)
{
obj.set(index,value);
return*this;
}
setter(foo&o, int i)
: obj(o), index(i) {}
};
int operator[](int index) const // getter
{ return data[index]; }
setter operator[](int index) // setter
{ return {*this,index}; }
};
If what you are trying to do is overload [] where you can input info like a dict or map like dict[key] = val. The answer is actually pretty simple:
lets say you want to load a std::string as the key, and std::vector as the value.
and lets say you have an unordered_map as your underlying structure that you're trying to pass info to
std::unordered_map<std::string, std::vector<double>> myMap;
Inside your own class, you have this definition:
class MyClass{
private:
std::unordered_map<std::string, std::vector<double>> myMap;
public:
std::vector<double>& operator [] (std::string key) {
return myMap[key];
}
}
Now, when you want to load your object, you can simply do this:
int main() {
std::vector<double> x;
x.push_back(10.0);
x.push_back(20.0);
x.push_back(30.0);
x.push_back(40.0);
MyClass myClass;
myClass["hello world"] = x;
double x = myClass["hello world"][0]; //returns 10.0
}
The overloaded [] returns a reference to where that vector is stored. So, when you call it the first time, it returns the address of where your vector will be stored after assigning it with = x. The second call returns the same address, now returning the vector you had input.

Operator overloading on priority_queue of object pointers

class MyInteger
{
public:
MyInteger() { }
MyInteger(int val) { value = val }
int value;
bool operator<(const MyInteger* target) const
{
return value < target->value;
}
Above is an example MyInteger class with the < operator overloaded. I am using a priority_queue of MyInteger*but it doesn't seem to recognize the overloaded operator, which results in the elements never being ordered.
std::priority_queue<MyInteger*> myInts;
MyInteger integer1 = MyInteger(1);
MyInteger integer5 = MyInteger(5);
MyInteger integer3 = MyInteger(3);
myInts.push(&integer1);
myInts.push(&integer5);
myInts.push(&integer3);
// result is same order they went in
Is it possible to use operator overloading for object pointers? Does it mean I will have to create my own functor to be used instead?
As mentioned by Olaf Dietsche, as you store pointers, the objet adresses are used for sorting, so in most case, order in the container will be equal to order of creation.
You'd better store objects in the container. Then it's comparison operator will be used for ordering:
class MyInteger
{
public:
MyInteger() { }
MyInteger(int val) { value = val }
MyInteger( const MyInteger& val ) : value( val.value ) {}
int value;
bool operator<(const MyInteger& target) const
{
return value < target.value;
}
};
...
std::priority_queue<MyInteger> myInts;
MyInteger integer1 = MyInteger(1);
MyInteger integer5 = MyInteger(5);
MyInteger integer3 = MyInteger(3);
myInts.push(integer1);
myInts.push(integer5);
myInts.push(integer3);
Then objects will be ordered correctly (using operator<).
It is even safer in case integer1, integer2 or integer3 gets destroyed before myInts (as this one stores copies).
If you really need to save pointers, then you 'ill have to pass a custom comparator to the container. See declaring a priority_queue in c++ with a custom comparator.
This should work:
class CompareMyIntegerPtr
{
public:
bool operator() (MyInteger* left, MyInteger* right)
{
return left->value < right->value;
}
};
...
std::priority_queue<MyInteger*, std::vector<MyInteger*>, CompareMyIntegerPtr> myInts;
The order has nothing to do with insertion order, but with the addresses in memory.
This is because the comparison operator is applied to the addresses of the elements, e.g.
&integer1 < &integer5 < &integer3
The comparison operator of MyInteger is not considered here.

Polymorphism,STL,find and operator==

I ran into a problem.
I have a class A,and a class that inherits from A,lets call it class B.
I have virtual functions.
I want to compare A and B to another class C by operator==.
If i want to have a list of A's,lets say in stl list,
I must use a pointer to A,so it will look like:
list<*A> list;
and also i have: C something
but now,i cant use the function:find(list.begin(),list.end(),something)
because i cant use operator == for pointers(*).
I found a solution but i dont think its the best,so my question is-can i do it better?
iter=list.begin();
for(iter;iter!=list.end();++iter)
{
if((*iter).operator==(something)
return ...
}
Thank you.
You could use find_if, which lets you provide a function to check for equal values.
auto check_something =
[&something](const list<*A>::iterator& iter){return *iter == something; };
find_if(list.begin(),list.end(),check_something)
You can use
if(**iter == something)
if you want to dereference the pointer.
In C++1x, there is also
for(auto ptr : list)
if(*ptr == something)
Nothing says you can't make a global non-member operator == that operates on pointers or combinations of pointers and objects. If you have many types you could template the combination of pointer vs object equality for any type.
Edit to add this tip: Put the comparison in a namespace with your objects and then argument dependent lookup will find it without putting a global T* == T in scope that catches everything:
namespace equals {
struct A {
A(int x) :x(x) { }
bool operator == (const A &other) const {
return other.x == x;
}
int x;
};
template <typename T>
bool operator == (const T *lhs, const T &rhs) {
return *lhs == rhs;
}
template <typename T>
bool operator == (const T &lhs, const T *rhs) {
return lhs == *rhs;
}
}
Now you can do things like:
equals::A b(1), c(1);
if (b == &c) std::cerr << "good!" << std::endl;
You might have a look at boost::indirect_iterator which is designed for just this purpose.
find(
boost::make_indirect_iterator( list.begin() ),
boost::make_indirect_iterator( list.end() ),
something );

Why can't you put a const object into a STL container?

See the code below - I am trying to put a const object into a vector. I know the answer is "STL containers require objects to be assignable and copy constructable", but, without citing the standard, can anyone explain what the problem with doing this is? I don't understand why a class like this could not be copied (besides that c++ doesn't allow it).
All it is is a value stored that is not allowed to be changed - why can't putting it in a vector simply create another one of these objects?
#include <vector>
// Attempt 1
// /home/doriad/Test/Test.cxx:3:8: error: non-static const member ‘const int MyClass::x’, can’t use default assignment operator
// struct MyClass
// {
// int const x;
// MyClass(int x): x(x) {}
// };
//
// int main()
// {
// std::vector<MyClass> vec;
// vec.push_back(MyClass(3));
// return 0;
// }
// Attempt 2
// /home/doriad/Test/Test.cxx:28:23: error: assignment of read-only member ‘MyClass::x’
struct MyClass
{
int const x;
MyClass(int x): x(x) {}
MyClass& operator= (const MyClass& other)
{
if (this != &other)
{
this->x = other.x;
}
return *this;
}
};
int main()
{
std::vector<MyClass> vec;
vec.push_back(MyClass(3));
return 0;
}
EDIT:
It is possible to do this with std::set and std::list. I guess it is the sort() function in std::vector that uses assignment. This is not UB right?
#include <set>
// Attempt 1
struct MyClass
{
int const x;
MyClass(int x): x(x) {}
bool operator< (const MyClass &other) const;
};
bool MyClass::operator<(const MyClass &other) const
{
if(this->x < other.x)
{
return true;
}
else if (other.x < this->x)
{
return false;
}
}
int main()
{
std::set<MyClass> container;
container.insert(MyClass(3));
return 0;
}
EDIT2: (Removing a bunch of stuff that doesn't have to work) The C++11 standard states that the insert method for vector and deque (and the default implementation of push_back for that matter) requires the value type to be CopyAssignable, i.e., the value supports:
t= v;
Classes and structs with const members are not CopyAssignable by default, so what you want to do won't work.
This doc (n3173) has an explanation for the various container requirements.
One possible solution would be to store pointers to the objects in the vector, because pointers are assignable and copy constructable.
Another possible solution would be to declare x without the const keyword, but ensure that it cannot be modified through encapsulation (i.e. you should declare it as private and don't modify from anywhere outside the constructor)..
When you place an object of type MyClass in the std::vector, the vector will make a copy of the object for storage, and not the object you passed to it.