Unqualified sort () - dlaczego kompiluje się, gdy jest używany na std :: vector, a nie na std :: array, i który kompilator jest poprawny?

11

Dzwoniąc std::sort()na std::array:

#include <vector>
#include <array>
#include <algorithm>

int main() {
    std::vector<int> foo{4, 1, 2, 3};
    sort(begin(foo), end(foo));

    std::array<int, 4> foo2{4, 1, 2, 3};
    sort(begin(foo2), end(foo2));
}

Zarówno gcc, jak i clang zwracają błąd podczas sortowania w opcji std::array- mówi clang

błąd: użycie niezadeklarowanego identyfikatora „sort”; miałeś na myśli „std :: sort”?

Zmiana na std::sort(begin(foo2), end(foo2))naprawia problem.

MSVC kompiluje powyższy kod zgodnie z opisem.

Dlaczego różnica w traktowaniu między std::vectori std::array; i który kompilator jest poprawny?

Guy Middleton
źródło
sort(...-> std::sort(.... Wydaje mi się, że to ADL (wyszukiwanie zależne od argumentów) jest tym, co cię potyka. To lub przewodniki dedukcyjne. W każdym przypadku; zawsze określ funkcje, które wywołujesz.
Jesper Juhl
3
Może być tak, że biblioteka MSVC ma jakąś specjalizację, std::sortktóra prowadzi do wyszukiwania zależnego od argumentów (tak jak już masz dla std::begini std::end)?
Jakiś programista koleś
1
@Someprogrammerdude Po prostu wszystkie kontenery w stdlib VC ++ używają iteratorów typu klasy zdefiniowanych namespace stdnawet tam, gdzie działałby prosty typ wskaźnika. Uważam, że ma to na celu wstawienie kontroli kompilacji debugowania w celu wykrycia przekroczeń i innych typowych błędów.
François Andrieux

Odpowiedzi:

16

Sprowadza się to do typu begini endwyniku oraz sposobu, w jaki działa to z Argument Dependent Lookup .

W

sort(begin(foo), end(foo));

dostajesz

sort(std::vector<int>::iterator, std::vector<int>::iterator)

a ponieważ std::vector<int>::iteratorjest członkiem stdADL znalezisk sortw stdi wywołanie się powiedzie.

Z

sort(begin(foo2), end(foo2));

Dostajesz

sort(int*, int*)

a ponieważ int*nie jest członkiem std, ADL nie zajrzy stdi nie będzie można go znaleźć std::sort.

Działa to w MSVC, ponieważ

sort(begin(foo2), end(foo2));

staje się

sort(std::_Array_iterator, std::_Array_iterator)

i ponieważ std::_Array_iteratorjest częścią stdznalezisk ADL sort.

Oba kompilatory są zgodne z tym zachowaniem. std::vectori std::arraynie ma żadnych wymagań dotyczących tego, jaki typ jest używany dla iteratora, z wyjątkiem tego, że spełnia on wymaganie LegacyRandomAccessIterator, aw C ++ 17 dla std::arraytego typu również typ LiteralType, aw C ++ 20, że będzie to ConstexprIterator

NathanOliver
źródło
1
Myślę, że pytanie brzmi, czy zachowanie MSVC jest zgodne, tj. Czy std::arrayiterator musi być, int*czy może być typem klasy? Podobnie std::vectorbyłoby istotne dla pytania, czy iterator musi być typem klasy, na którym będzie działać ADL, czy też może int*tak być .
orzech
@walnut Może to być wszystko, co chce implementacja. Może to być std::iteratorcoś innego lub wskaźnik.
NathanOliver
1
Zastanawiam się także, dlaczego w tej implementacji biblioteki zdecydowali się używać int*do, std::arrayale nie do std::vector.
François Andrieux
1
Rodzaje iterator zarówno std::arrayi std::vectorsą nieokreślone, co oznacza realizację wolno definiować je jako surowych wskaźników (kod nie będzie kompilować) lub klasa typu owijarki (kod zostanie skompilowany tylko jeśli typ klasa ma stdjako związanego nazw ADL).
aschepler
1
Oto demonstracja, w której ADL nie działa z aliasem, a tutaj jest demonstracja, w której ADL odnosi sukces z klasą zagnieżdżoną. Zarówno tutaj, jak i we wcześniejszych testach std::vector<T>::iteratorjest pseudonimem.
użytkownik2357112 obsługuje Monikę