Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

namespace std overloading less than

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!

like image 485
Billy Won Avatar asked May 08 '15 00:05

Billy Won


People also ask

Does overloading == work for !=?

Overloading operator == does not give you operator !=

Can we overload == operator in C++?

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.

Can we overload sizeof in C++?

sizeof cannot be overloaded because built-in operations, such as incrementing a pointer into an array implicitly depends on it.

Which function overloads the Greater Than operator?

C++ Program to overload the Greater than > operator In this program we try to overload the > operator with C++.


1 Answers

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.

like image 116
T.C. Avatar answered Oct 04 '22 04:10

T.C.