I was curious about why this piece of code doesn't work:
#include "stdafx.h"
#include <iostream>
#include <tuple>
#include <string>
#include <vector>
#include <algorithm>
typedef std::tuple<int, std::string> intString;
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
void printIntStrings(std::vector<intString>& v){
for (intString& i : v){
std::cout << std::get<0>(i) << " is " << std::get<1>(i) << std::endl;
}
}
int main(int argc, char* argv[])
{
std::vector<intString> v;
v.push_back(std::make_tuple(5, "five"));
v.push_back(std::make_tuple(2, "two"));
v.push_back(std::make_tuple(9, "nine"));
printIntStrings(v);
std::sort(v.begin(), v.end());
printIntStrings(v);
return 0;
}
As far as I can understand, I simply create a vector of intStrings and my operator should sort by the second element in the tuple first thus the output should be (last 3 lines anyway)
5 five
9 nine
2 two
However running it on my machine I get
2 two
5 five
9 nine
which implies that the sort is using the default less than operator, ignoring the one I specified. Note, adding const before the parameters didn't seem to affect anything.
I found three ways to "fix" this.
Fix #1
surround bool operator< ... in namespace std like so:
namespace std{
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
}
However I was told we should never add things to the std namespace since that behavior is undefined, so this fix seems the worst.
Fix #2
Add in something custom to the tuple like so:
enum class TRASH{DOESNTMATTER};
typedef std::tuple<int, std::string, TRASH> intString;
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
(and obviously add in TRASH::DOESNTMATTER as the third make_tuple argument) However, this seemed like a lot of work for something this simple. Also, it seems wasteful since the enum is not meaningfully used.
Fix #3
Use the predicate sort like so:
std::sort(v.begin(), v.end(), operator<);
This seemed to be the most elegant solution. However, I don't see why I have to explicitly tell the compiler to use my defined operator<.
So I want to know:
1) why this happens? Shouldn't c++ find my implementation and use that?
2) which "fix" is the best? if none of the ones I found, what would you recommend?
Any ideas? Thanks for reading!
Overloading operator == does not give you operator !=
Overloading Binary Operators Suppose that we wish to overload the binary operator == to compare two Point objects. We could do it as a member function or non-member function. To overload as a member function, the declaration is as follows: class Point { public: bool operator==(const Point & rhs) const; // p1.
sizeof cannot be overloaded because built-in operations, such as incrementing a pointer into an array implicitly depends on it.
C++ Program to overload the Greater than > operator In this program we try to overload the > operator with C++.
Your operator<
overload is not visible at the point where <
is used (which is in the body of std::sort
and/or any helper functions called by it, somewhere in <algorithm>
).
If it is to be used, it must be picked up by argument-dependent lookup; but there's nothing in std::tuple<int, std::string>
that has the global namespace as an associated namespace, so ADL doesn't help you, either, and the standard one is used.
Pass it as a comparator, preferably using a lambda or function object (which inlines better than function pointers), is the simplest fix. I'd also recommend renaming it; having a operator<
overload with completely different semantics than the standard one, which may or may not be used by the expression a < b
depending on where that expression is, is not a good idea.
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