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.
Related
In my program I want that the default value, if a key doesn't exist in my unordered map, is going to be std::numeric_limits<double>::max() Instead of just 0.
my code looks like the following:
class Pair
{
public:
Graph::NodeId node;
bitset<64> bits;
Pair(Graph::NodeId node1, double bits1)
{
node = node1;
bitset<64> temp(bits1);
bits = temp;
}
};
bool operator==(const Pair &P1, const Pair &P2)
{
return P1.node == P2.node && P1.bits == P2.bits;
}
template <>
struct std::hash<Pair>
{
std::size_t operator()(const Pair &pair) const noexcept
{
std::hash<decltype(pair.node)> ihash;
std::hash<decltype(pair.bits)> bhash;
return ihash(pair.node) * 31 + bhash(pair.bits);
}
};
unordered_map<Pair, double> lFunction;
so if I want to access the element lFunction[Pair(3,3)] and the key doesn't exist, there should be the value std::numeric_limits<double>::max().
One possibility would be to create a tiny class for the value type:
class dproxy {
double value_;
public:
dproxy(double value = std::numeric_limits<double>::max())
: value_{value} {}
operator double &() { return value_; }
operator double const &() const { return value_; }
};
std::unordered_map<Pair, dproxy> lFunction;
A default-constructed dproxy will be initialized to std::numeric_limits<double>::max(), and a dproxy will implicitly convert to a (reference to a) double. This can impose limitations if you're doing a lot of implicit conversions otherwise though (e.g., if you had some other function foo that took some other type bar that could be implicitly constructed from a double, foo(lFunction[baz]); would work if lFunction contained doubles, but not if it contained dproxy objects, since an implicit conversion sequence can only use one user-defined conversion).
if I want to access the element lFunction[Pair(3,3)] and the key doesn't exist, there should be the value std::numeric_limits<double>::max().
std::unordered_map::operator[] simply does not work that way. When it needs to insert a new value for a missing key, the new value is always value-initialized, which for numeric types means 0. There is no option to change that behavior in the unordered_map itself. But #JerryCoffin's answer shows you a workaround to take advantage of that behavior so you can define your own default value.
Otherwise, the alternative is to simply not use the map's operator[] at all. Use its find() and emplace() methods instead, eg:
auto getFunctionValue = [&](const Pair &key)
{
auto iter = lFunction.find(key);
if (iter == lFunction.end()) {
iter = lFunction.emplace(key, std::numeric_limits<double>::max()).first;
// or simply:
// return std::numeric_limits<double>::max();
}
return iter->second;
};
//double d = lFunction[Pair(3,3)];
double d = getFunctionValue(Pair(3,3));
...
Or, if you are using C++17 or later, you can use the try_emplace() method instead, eg:
auto getFunctionValue = [&](const Pair &key)
{
return lFunction.try_emplace(key, std::numeric_limits<double>::max()).first->second;
};
//double d = lFunction[Pair(3,3)];
double d = getFunctionValue(Pair(3,3));
...
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.
So for class this week I have to use a set to read in the Declaration of Independence and the US Constitution from a .txt file, store them in sets, and overload operator* to find and return the intersection of the two sets.
Reading everything in is not a problem, neither is finding out the intersection in the sets. What I'm having a lot of trouble with is overloading operator*. I keep getting two errors:
no operator "*" matches these operands
and
binary "*":'std::set<_Kty>' does not define this operator or a conversion to a type acceptable to the predefined operator
Here is my code so far:
From main:
Reader r;
std::set<std::string> d, c;
d = r.GetDeclaraton();
c = r.GetConstitution();
Set dec(d), con(c);
Set intersection = dec * con;
The errors are coming from that last line of main.
From Set.h
class Set
{
public:
std::set<std::string> s;
Set(void);
Set(std::set<std::string> set)
{
s = set;
}
~Set(void);
std::set<std::string> operator* (const std::set<std::string> &rhs)
{
std::set<std::string> newset;
std::set<std::string>::iterator rhsiter = rhs.begin(), result;
while (rhsiter != rhs.end())
{
result = s.find(*rhsiter++);
if (result != rhs.end())
{
newset.insert(*result);
}
}
return newset;
}
};
You need
Set operator* (const Set &rhs) const
because this is a binary operator with two Sets as arguments. Your attempt would only work if your Set was deriving std::set.
The return type here is Set. It makes more sense to be the same as the input arguments, and this is also consistent with your usage in main. So in this case you need to modify the definition to construct and return a Set instead of an std::set (or rather you need not because an std::set can be implicitly converted to a Set).
Note also I made operator* a const member function, since it's not modifying its object.
operator* is defined in your custom Set so the left-hand-side argument must be a Set, but in dec.s * con.s you're using .s which accesses the member std::set fields, and there's no operator* defined for a left-hand Set and rhs-hand std::set.
You could change to dec * con.s, but it'd be better to change the operator* rhs argument to a const Set& too, and use rhs.s inside the operator... less confusing!
In my opinion its better to write a global or friend operator. Also, for intersection I would use the & operator, for union the | or + operator while, difference: -...
Why global operators? With global operators you can write an operator for your class even if the left operand is not your class. For example in case of a matrix class you can easily write a global operator that multiplies together a float and a matrix while as a matrix member operator you could write only an operator where your matrix is on the left.
Example:
template <typename T>
inline std::set<T> intersection(const std::set<T>& smaller, const std::set<T>& larger)
{
std::set<T> result;
for (auto it=smaller.begin(),eit=smaller.end(); it!=eit; ++it)
{
if (larger.find(*it) != larger.end())
result.insert(*it);
}
return result;
}
template <typename T>
inline std::set<T> operator & (const std::set<T>& a, const std::set<T>& b)
{
if (a.size() < b.size())
return intersection(a, b);
return intersection(b, a);
}
class Set
{
public:
std::set<std::string> s;
Set() {}
Set(std::set<std::string> _s) : s(_s) {}
friend Set operator & (const Set& a, const Set& b)
{
return Set(a.s & b.s);
}
};
int test()
{
std::set<std::string> s, t;
s.insert("aa");
s.insert("bb");
t.insert("bb");
t.insert("cc");
std::set<std::string> u(s & t);
Set ss(s), st(t);
Set result(ss & st);
return 0;
}
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.)
I am currently in a collage second level programing course... We are working on operator overloading... to do this we are to rebuild the vector class...
I was building the class and found that most of it is based on the [] operator. When I was trying to implement the + operator I run into a weird error that my professor has not seen before (apparently since the class switched IDE's from MinGW to VS express...) (I am using Visual Studio Express 2008 C++ edition...)
Vector.h
#include <string>
#include <iostream>
using namespace std;
#ifndef _VECTOR_H
#define _VECTOR_H
const int DEFAULT_VECTOR_SIZE = 5;
class Vector
{
private:
int * data;
int size;
int comp;
public:
inline Vector (int Comp = 5,int Size = 0)
: comp(Comp), size(Size) { if (comp > 0) { data = new int [comp]; }
else { data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE; }
}
int size_ () const { return size; }
int comp_ () const { return comp; }
bool push_back (int);
bool push_front (int);
void expand ();
void expand (int);
void clear ();
const string at (int);
int& operator[ ](int);
int& operator[ ](int) const;
Vector& operator+ (Vector&);
Vector& operator- (const Vector&);
bool operator== (const Vector&);
bool operator!= (const Vector&);
~Vector() { delete [] data; }
};
ostream& operator<< (ostream&, const Vector&);
#endif
Vector.cpp
#include <iostream>
#include <string>
#include "Vector.h"
using namespace std;
const string Vector::at(int i) {
this[i];
}
void Vector::expand() {
expand(size);
}
void Vector::expand(int n ) {
int * newdata = new int [comp * 2];
if (*data != NULL) {
for (int i = 0; i <= (comp); i++) {
newdata[i] = data[i];
}
newdata -= comp;
comp += n;
data = newdata;
delete newdata;
}
else if ( *data == NULL || comp == 0) {
data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE;
size = 0;
}
}
bool Vector::push_back(int n) {
if (comp = 0) { expand(); }
for (int k = 0; k != 2; k++) {
if ( size != comp ){
data[size] = n;
size++;
return true;
}
else {
expand();
}
}
return false;
}
void Vector::clear() {
delete [] data;
comp = 0;
size = 0;
}
int& Vector::operator[] (int place) { return (data[place]); }
int& Vector::operator[] (int place) const { return (data[place]); }
Vector& Vector::operator+ (Vector& n) {
int temp_int = 0;
if (size > n.size_() || size == n.size_()) { temp_int = size; }
else if (size < n.size_()) { temp_int = n.size_(); }
Vector newone(temp_int);
int temp_2_int = 0;
for ( int j = 0; j <= temp_int &&
j <= n.size_() &&
j <= size;
j++) {
temp_2_int = n[j] + data[j];
newone[j] = temp_2_int;
}
////////////////////////////////////////////////////////////
return newone;
////////////////////////////////////////////////////////////
}
ostream& operator<< (ostream& out, const Vector& n) {
for (int i = 0; i <= n.size_(); i++) {
////////////////////////////////////////////////////////////
out << n[i] << " ";
////////////////////////////////////////////////////////////
}
return out;
}
Errors:
out << n[i] << " "; error C2678:
binary '[' : no operator found which
takes a left-hand operand of type
'const Vector' (or there is no
acceptable conversion)
return newone;
error C2106: '=' : left
operand must be l-value
As stated above, I am a student going into Computer Science as my selected major I would appreciate tips, pointers, and better ways to do stuff :D
This:
int operator[ ](int);
is a non-const member function. It means that it cannot be called on a const Vector.
Usually, the subscript operator is implemented such that it returns a reference (if you return a value, like you are doing, you can't use it as an lvalue, e.g. you can't do newone[j] = temp_2_int; like you have in your code):
int& operator[](int);
In order to be able to call it on a const object, you should also provide a const version of the member function:
const int& operator[](int) const;
Since you ask for "tips, pointers, and better ways to do stuff:"
You cannot name your include guard _VECTOR_H. Names beginning with an underscore followed by a capital letter are reserved for the implementation. There are a lot of rules about underscores.
You should never use using namespace std in a header.
Your operator+ should take a const Vector& since it is not going to modify its argument.
Your at should return an int and should match the semantics of the C++ standard library containers (i.e., it should throw an exception if i is out of bounds. You need to use (*this)[i] to call your overloaded operator[].
You need to learn what the * operator does. In several places you've confused pointers and the objects to which they point.
Watch out for confusing = with == (e.g. in if (comp = 0)). The compiler will warn you about this. Don't ignore warnings.
Your logic will be much simpler if you guarantee that data is never NULL.
Can't fit this into a comment on Neil's answer, so I'm gonna have to go into more detail here.
Regarding your expand() function. It looks like this function's job is to expand the internal storage, which has comp elements, by n elements, while maintaining the size of the Vector. So let's walk through what you have.
void Vector::expand(int n) {
int * newdata = new int [comp * 2];
Okay, you just created a new array that is twice as big as the old one. Error: Why doesn't the new size have anything to do with n?
if (*data != NULL) {
Error: *data is the first int element in your array. It's not a pointer. Why is it being compared to NULL?
Concept Error: Even if you said if (data != NULL), which could be a test to see if there is an array at all, at what point in time is data ever set to NULL? new [] doesn't return NULL if it's out of memory; it throws an exception.
for (int i = 0; i <= (comp); i++) {
newdata[i] = data[i];
}
Warning: You're copying the whole array, but only the first size elements are valid. The loop could just run up to size and you'd be fine.
newdata -= comp;
Error: Bad pointer math. newdata is set to a pointer to who knows where (comp ints back from the start of newdata?!), and almost certainly a pointer that will corrupt memory if given to delete [].
comp += n;
This is fine, for what it is.
data = newdata;
delete newdata;
}
Error: You stored a pointer and then immediately deleted its memory, making it an invalid pointer.
else if ( *data == NULL || comp == 0) {
data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE;
size = 0;
}
}
Error: This should be in your constructor, not here. Again, nothing ever sets data to NULL, and *data is an int, not a pointer.
What this function should do:
create a new array of comp + n elements
copy size elements from the old array to the new one
delete the old array
set data to point to the new array
Good luck.
Besides of what others already wrote about your operator[]():
Your operator+() takes the right-hand side per non-const reference - as if it would attempt to change it. However, with A+B everyone would expect B to remain unchanged.
Further, I would implement all binary operators treating their operands equally (i.e., not changing either of them) as non-member functions. As member functions the left-hand side (this) could be treated differently. (For example, it could be subjected to overwritten versions in derived classes.)
Then, IME it's always good to base operator+() on operator+=(). operator+=() does not treat its operands equally (it changes its left one), so it's best done as a member function. Once this is done, implementing operator+() on top of it is a piece of cake.
Finally, operator+() should never, never ever return a reference to an object. When you say A+B you expect this to return a new object, not to change some existing object and return a reference to that.
There are so many errors in your code that it is hard to know where to start. Here's one:
delete [] data;
*data = *newdata;
You delete a pointer and then immediately dereference it.
And:
const string Vector::at(int i) {
this[i];
}
This is (I think) a vector of ints. why is this returning a string? And applying the [] operator to this does not call your operator[] overload - it treats this as an array, which it isn't.
You need to provide two versions of your operator[]. For accessing:
T operator[](std::size_t idx)const;
For writing to the element:
T& operator[](std::size_t idx);
In both of the above, replace T with the type of the elements. The reason you have this problem is that only functions that are marked "const" may be invoked on an object declared to be "const". Marking all non-mutating functions as "const" is definitely something you should do and is called "const-correctness". Since returning a reference to an element (necessary for writing) allows the underlying object to be mutated, that version of the function cannot be made "const". Therefore, a read-only "const" overload is needed.
You may also be interested in reading:
Const Correctness from the C++ FAQ Lite
Const Correctness in C++
int Vector::operator[] (int place) { return (data[place]); }
This should be
int Vector::operator[] (int place) const { return (data[place]); }
so that you will be able to do the [] operation on const vectors. The const after the function declaration means that the class instance (this) is treated as const Vector, meaning you won't be able to modify normal attributes. Or in other words: A method that only has read access to attributes.