Namespace std overload less than
I was curious why this part of the code does not 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 understand, I just create an intStrings vector, and my operator should sort by the second element in the tuple, so there should be an output (last 3 lines anyway)
5 five 9 nine 2 two However, by running it on my machine, I get
2 two 5 five 9 nine which means that sorting uses a default value less than the operator, ignoring the one I specified. Note that adding const before the parameters did not affect anything.
I found three ways to "fix" this.
Fix # 1
The environment statement bool <... in the std namespace is as follows: 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 that we should never add things to the std namespace, as this behavior is undefined, so this fix seems to be worse.
Fix # 2
Add something normal to the tuple, for example:
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 TRASH :: DOESNTMATTER as the third argument to make_tuple) However, this seemed like a lot of work for something so simple. In addition, this seems wasteful since listing does not make sense.
Fix # 3
Use a predicate like:
std::sort(v.begin(), v.end(), operator<); This seemed like the most elegant solution. However, I do not understand why I should explicitly tell the compiler to use my specific <operator.
So I want to know:
1) why is this happening? Shouldn't C ++ find my implementation and use it?
2) which “fix” is the best? if not one of those that I found, what would you recommend?
Any ideas? Thank you for reading!
The operator< overload is not visible at the point where < used (which is located in the body of std::sort and / or any auxiliary functions called by it, somewhere in <algorithm> ).
If used, it should be picked up depending on the argument of the search; but in std::tuple<int, std::string> there is nothing that has a global namespace as an associated namespace, so ADL will not help you, and the standard one is used.
Pass it as a comparator, preferably using a lambda or function object (which is better in lines than function pointers) is the easiest fix. I also recommend renaming it; with operator< overloading with completely different semantics than the standard one, which may or may not be used by the expression a < b depending on where this expression is, is not a good idea.
you already fix it yourself
the problem is your operator <function does not override the standard tuple :: operator <, they are in a different namespace
So both fix # 1 and fix # 3 are a good solution
Fix # 1 put them in the same namespace make it override correctly, I think this is the best way