Defining Operator< for a Struct

Defining operator for a struct

This is quite an old question and as a consequence all answers here are obsolete. C++11 allows a more elegant and efficient solution:

bool operator <(const MyStruct& x, const MyStruct& y) {
return std::tie(x.a, x.b, x.c) < std::tie(y.a, y.b, y.c);
}

Why is this better than using boost::make_tuple? Because make_tuple will create copies of all the data members, which can be costly. std::tie, by contrast, will just create a thin wrapper of references (which the compiler will probably optimise away entirely).

In fact, the above code should now be considered the idiomatic solution to implementing a lexicographical compare for structures with several data members.

Overloading operators in typedef structs (c++)

The breakdown of your declaration and its members is somewhat littered:

Remove the typedef

The typedef is neither required, not desired for class/struct declarations in C++. Your members have no knowledge of the declaration of pos as-written, which is core to your current compilation failure.

Change this:

typedef struct {....} pos;

To this:

struct pos { ... };

Remove extraneous inlines

You're both declaring and defining your member operators within the class definition itself. The inline keyword is not needed so long as your implementations remain in their current location (the class definition)


Return references to *this where appropriate

This is related to an abundance of copy-constructions within your implementation that should not be done without a strong reason for doing so. It is related to the expression ideology of the following:

a = b = c;

This assigns c to b, and the resulting value b is then assigned to a. This is not equivalent to the following code, contrary to what you may think:

a = c;
b = c;

Therefore, your assignment operator should be implemented as such:

pos& operator =(const pos& a)
{
x = a.x;
y = a.y;
return *this;
}

Even here, this is not needed. The default copy-assignment operator will do the above for you free of charge (and code! woot!)

Note: there are times where the above should be avoided in favor of the copy/swap idiom. Though not needed for this specific case, it may look like this:

pos& operator=(pos a) // by-value param invokes class copy-ctor
{
this->swap(a);
return *this;
}

Then a swap method is implemented:

void pos::swap(pos& obj)
{
// TODO: swap object guts with obj
}

You do this to utilize the class copy-ctor to make a copy, then utilize exception-safe swapping to perform the exchange. The result is the incoming copy departs (and destroys) your object's old guts, while your object assumes ownership of there's. Read more the copy/swap idiom here, along with the pros and cons therein.


Pass objects by const reference when appropriate

All of your input parameters to all of your members are currently making copies of whatever is being passed at invoke. While it may be trivial for code like this, it can be very expensive for larger object types. An exampleis given here:

Change this:

bool operator==(pos a) const{
if(a.x==x && a.y== y)return true;
else return false;
}

To this: (also simplified)

bool operator==(const pos& a) const
{
return (x == a.x && y == a.y);
}

No copies of anything are made, resulting in more efficient code.


Finally, in answering your question, what is the difference between a member function or operator declared as const and one that is not?

A const member declares that invoking that member will not modifying the underlying object (mutable declarations not withstanding). Only const member functions can be invoked against const objects, or const references and pointers. For example, your operator +() does not modify your local object and thus should be declared as const. Your operator =() clearly modifies the local object, and therefore the operator should not be const.


Summary

struct pos
{
int x;
int y;

// default + parameterized constructor
pos(int x=0, int y=0)
: x(x), y(y)
{
}

// assignment operator modifies object, therefore non-const
pos& operator=(const pos& a)
{
x=a.x;
y=a.y;
return *this;
}

// addop. doesn't modify object. therefore const.
pos operator+(const pos& a) const
{
return pos(a.x+x, a.y+y);
}

// equality comparison. doesn't modify object. therefore const.
bool operator==(const pos& a) const
{
return (x == a.x && y == a.y);
}
};

EDIT OP wanted to see how an assignment operator chain works. The following demonstrates how this:

a = b = c;

Is equivalent to this:

b = c;
a = b;

And that this does not always equate to this:

a = c;
b = c;

Sample code:

#include <iostream>
#include <string>
using namespace std;

struct obj
{
std::string name;
int value;

obj(const std::string& name, int value)
: name(name), value(value)
{
}

obj& operator =(const obj& o)
{
cout << name << " = " << o.name << endl;
value = (o.value+1); // note: our value is one more than the rhs.
return *this;
}
};

int main(int argc, char *argv[])
{

obj a("a", 1), b("b", 2), c("c", 3);

a = b = c;
cout << "a.value = " << a.value << endl;
cout << "b.value = " << b.value << endl;
cout << "c.value = " << c.value << endl;

a = c;
b = c;
cout << "a.value = " << a.value << endl;
cout << "b.value = " << b.value << endl;
cout << "c.value = " << c.value << endl;

return 0;
}

Output

b = c
a = b
a.value = 5
b.value = 4
c.value = 3
a = c
b = c
a.value = 4
b.value = 4
c.value = 3

Where to declare a struct operator overloading

You’re mixing up declaration and definition in your code. Put the definitions into the implementation file (*.cpp). Put the declarations into the header, next to your declaration of round.

Alternatively, you could put the definitions into the header and declare them inline (and this is routine for short functions such as custom operators). The inline specifier on functions prevents functions from violating the One Definition Rule when included by multiple translation units.

operator== in C++ struct

When overloading an binary operator as a member function, the first parameter is this pointer. In the signature you have defined the operator==, it will take 3 arguments. However, it can only take two.

In you case I would recommend making it a non-member function.

typedef struct _tBScan {
QString strQuadpackNumbers;
uint uintBegin, uintEnd;
} tBScan;

bool operator==(const struct _tBScan& a, const struct _tBScan& b) {
return (a.strQuadpackNumbers.compare(b.strQuadpackNumbers) == 0 &&
a.uintBegin == b.uintBegin && a.uintEnd == b.uintEnd);
}

When you overload the, let's say, operator@ the expression _tBScan @ _smt is resolved into.

_tBScan.operator@(_smt);

When it's not a member function the expression is resolved into

operator@(_tBScan, _smt);

So the compiler searches the overload of either of them.

How to properly overload operator in a struct to enable use in a set in C++

Did you look at the compile error?

As it says, the method needs to be const

bool operator<(const Coordinate& other)  const {   // const the < operator
return this->x * this->y < other.x * other.y;
}

How can I add operator definition to an existing struct from a header file?

You can simply use custom functor in your unordered_map declaration.

Indeed, unordered_map provides custom template arguments for this purpose.

Note: you need to provide a functor to compute the hash value too for unordered_map.

template<
class Key,
class T,
class Hash = std::hash<Key>, // <----- You need hash for Key
class KeyEqual = std::equal_to<Key>, // <---- Equal Functor
// ...

Just define your functor in accordance with the logic of your program. Something like:

struct AsnObjectIdentifierHasher {
std::size_t operator()(const AsnObjectIdentifier&) const noexcept;
};

struct AsnObjectIdentifierComparator {
bool operator()(const AsnObjectIdentifier&,
const AsnObjectIdentifier&) const noexcept;
};

template <typename T>
using HashMap = std::unordered_map<AsnObjectIdentifier,
T,
AsnObjectIdentifierHasher,
AsnObjectIdentifierComparator>;

I strongly discourage the usage of a free-function to implement arithmetic and logical operators (especially when STL interface provides customization by means of template arguments). Functors (function objects) provide better isolation code and avoid scope problems (e.g., ADL).

== operator overloading with struct

If you overload a binary operator as a member function, then it should only take one argument. The first operand is the object the operator is called on (i.e. *this); the second operand is the single function argument.

struct names {
//...

// better to pass by reference;
// make the function 'const' so it can be used on constant objects
bool operator==(names const & rhs) const {
return this->fname == rhs.lname;
}
};

Alternatively, you can overload it as a non-member function, with two arguments:

bool operator==(names const & lhs, names const & rhs) {
return lhs.fname == rhs.lname;
}

If this needed access to private members (which isn't the case in this example), then it would have to be a friend. You can define friends inside the class definition; in which case the code would look exactly the same as your example, only with friend in front of the function declaration.

(Of course, this isn't a sensible definition of equality since it's not symmetric. Many algorithms will break if you can have a == b but not b==a, as you can with this definition. lhs.fname == rhs.fname && lhs.lname == rhs.lname would make more sense.)



Related Topics



Leave a reply



Submit