2015-05-08 17 views
6

byłem ciekaw dlaczego ten fragment kodu nie działa:namespace std przeciążenia mniej niż

#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; 
} 

O ile mogę zrozumieć, po prostu stworzyć wektor intStrings i mój operator powinien sortować według drugi element w krotce pierwszy więc wyjście powinno być (ostatnie 3 linie tak)

5 five 
9 nine 
2 two 

jednak działa to na moim komputerze mam

2 two 
5 five 
9 nine 

co oznacza, że ​​sortowanie używa wartości domyślnej mniejszej niż operator, ignorując podaną przeze mnie. Zauważ, dodając const, zanim parametry nie wpłynęły na nic.

Znalazłem trzy sposoby "naprawienia" tego.

Fix # 1

bool operator przestrzenny < ... w przestrzeni nazw std tak:

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)); 
    } 
} 

Jednak powiedziano mi, że nigdy nie powinno się dodać rzeczy do przestrzeni nazw std ponieważ zachowanie jest undefined, więc ta poprawka wydaje się najgorsza.

Fix # 2

Dodaj coś zwyczaj krotki tak:

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)); 
} 

(i oczywiście dodać w Koszu :: DOESNTMATTER jako trzeci argument make_tuple) Jednak wydawało jak dużo pracy za coś tak prostego. Wydaje się też, że marnotrawstwo nie jest sensownie używane.

Fix # 3

Użyj rodzaju źródłowe tak:

std::sort(v.begin(), v.end(), operator<); 

To wydawało się być najbardziej eleganckie rozwiązanie. Jednak nie widzę powodu, dla którego muszę wyraźnie powiedzieć kompilator, aby użyć mojego zdefiniowanego operatora <.

Więc chcę wiedzieć:

1) dlaczego tak się dzieje? Nie powinienem C++ znaleźć mojej implementacji i użyć tego?

2) które "naprawić" jest najlepsze? jeśli żaden z tych, które znalazłem, co poleciłbyś?

Wszelkie pomysły? Dziękuje za przeczytanie!

+1

Standardowy operator '' jest lepiej dopasowany, ponieważ jego parametry to 'const'. – Oktalist

+0

@Oktalist Ups, zapomniałem wspomnieć o dodaniu stałej, zanim oba parametry nie pomogły. edytuje –

+0

Problem polega na tym, że istnieje'already_ 'operator <' dla krotek, więc kompilator używa oficjalnego zamiast hackera. –

Odpowiedz

6

Twój przeciążenie operator< nie jest widoczny w czasie t wskazuje miejsce, w którym jest używane < (które znajduje się w ciele std::sort i/lub dowolne funkcje pomocnicze przez niego wywoływane, gdzieś w <algorithm>).

Jeśli ma być używana, musi zostać pobrana przez wyszukiwanie zależne od argumentu; ale nie ma nic w std::tuple<int, std::string>, który ma globalną przestrzeń nazw jako skojarzoną przestrzeń nazw, więc ADL również nie pomaga i używa standardowego.

Przekazanie go jako komparatora, najlepiej przy użyciu obiektu lambda lub funkcji (który jest lepszy niż wskaźniki funkcji), jest najprostszą poprawką. Polecam również zmianę nazwy; posiadanie przeciążenia operator< z zupełnie inną semantyką niż standardowe, które może ale nie musi być używane przez wyrażenie a < b w zależności od tego, gdzie jest to wyrażenie, nie jest dobrym pomysłem.

+0

Ok, pomyślałem, że to coś takiego. Czy istnieje sposób, aby "zmusić" ADL, aby go podniósł? może coś na wzór globalnej przestrzeni nazw (wypróbowana przestrzeń nazw globalna i nie zadziałała) –

+1

@BillyWon Nie bez zmiany typu. Naprawdę nie polecam pisania '<', który robi coś innego niż ten, który dostarcza standardowa biblioteka. –

+0

Dzięki! Zmieni się na bardziej opisową nazwę. Gdyby był przeciążony

-1

już naprawić przez siebie

problemem jest twoja funkcja operator < nie zastąpić domyślny krotki :: operator <, są w innej przestrzeni nazw

więc, zarówno swoją Fix # 1 Fix i nr 3 są dobre rozwiązanie

Fix # 1 umieścić je w tej samej przestrzeni nazw uczynić go zastąpić poprawne, myślę, że jest to najlepszy sposób

+0

I powoduje niezdefiniowane zachowanie w procesie. Tak, to zdecydowanie najlepszy sposób. –

Powiązane problemy