Sort non qualifié () - pourquoi compile-t-il lorsqu'il est utilisé sur std :: vector et non sur std :: array, et quel compilateur est correct?

11

Lorsque vous faites appel std::sort()à 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));
}

Gcc et clang renvoient tous deux une erreur sur le tri sur std::array- clang dit

erreur: utilisation de l'identifiant non déclaré «tri»; vouliez-vous dire 'std :: sort'?

Changer pour std::sort(begin(foo2), end(foo2))résoudre le problème.

MSVC compile le code ci-dessus tel qu'il est écrit.

Pourquoi la différence de traitement entre std::vectoret std::array; et quel compilateur est correct?

Guy Middleton
la source
sort(...-> std::sort(.... Je suppose que l'ADL (recherche dépendante de l'argument) est ce qui vous fait trébucher. Ça, ou des guides de déduction. Dans tout les cas; qualifiez toujours les fonctions que vous appelez.
Jesper Juhl
3
Se pourrait-il que la bibliothèque MSVC ait une spécialisation std::sortqui mène à une recherche dépendante des arguments (comme vous l'avez déjà pour std::beginet std::end)?
Un programmeur mec le
1
@Someprogrammerdude C'est simplement que tous les conteneurs dans stdlib de VC ++ utilisent des itérateurs de type classe définis namespace stdmême là où un type de pointeur simple aurait fonctionné. Je crois que cela consiste à insérer des vérifications de build de débogage pour détecter les dépassements et autres erreurs courantes.
François Andrieux

Réponses:

16

Cela revient au type beginet au endrésultat et à la façon dont cela fonctionne avec la recherche dépendante de l'argument .

Dans

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

vous obtenez

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

et depuis std::vector<int>::iteratorest membre d' stdADL trouve sortdans stdet l'appel aboutit.

Avec

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

Vous obtenez

sort(int*, int*)

et parce qu'il int*n'est pas membre de std, ADL ne cherchera pas stdet vous ne trouverez pas std::sort.

Cela fonctionne dans MSVC car

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

devient

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

et puisque std::_Array_iteratorfait partie des stddécouvertes ADL sort.

Les deux compilateurs sont corrects avec ce comportement. std::vectoret std::arrayn'ont aucune exigence sur le type utilisé pour l'itérateur, sauf qu'il satisfait à l' exigence LegacyRandomAccessIterator et en C ++ 17 pour std::arrayque le type soit également un LiteralType et en C ++ 20 qu'il soit un ConstexprIterator

NathanOliver
la source
1
Je suppose que la question est de savoir si le comportement de MSVC est conforme, à savoir ce que le std::arrayiterator doivent être int*ou peut - il être un type de classe? De même, std::vectoril serait pertinent de se demander si l'itérateur doit être un type de classe sur lequel ADL fonctionnera, ou s'il peut l'être int*également.
noyer
@walnut Cela peut être ce que l'implémentation veut qu'il soit. Ce pourrait être un std::iterator, quelque chose d'autre, ou juste un pointeur.
NathanOliver
1
Je me demande également pourquoi dans cette implémentation de bibliothèque ils ont choisi d'utiliser int*pour std::arraymais pas pour std::vector.
François Andrieux
1
Les types d'itérateur pour les deux std::arrayet ne std::vectorsont pas spécifiés, ce qui signifie que l'implémentation est autorisée à les définir comme des pointeurs bruts (le code ne se compilera pas) ou des wrappers de type classe (le code se compilera uniquement si le type de classe a stdcomme espace de noms associé ADL).
aschepler
1
Voici une démo où l'ADL échoue avec un alias, et voici une démo où l'ADL réussit avec une classe imbriquée. Ici et dans mes tests précédents, std::vector<T>::iteratorest un alias.
user2357112 prend en charge Monica