#include ...">

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!

+6
source share
2 answers

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.

+6
source

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

-1
source

Source: https://habr.com/ru/post/986791/


All Articles