Classificação não qualificada () - por que compila quando usado em std :: vector e não em std :: array, e qual compilador está correto?

11

Ao chamar std::sort()um 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));
}

O gcc e o clang retornam um erro na classificação no std::array- clang diz

erro: uso do identificador não declarado 'sort'; você quis dizer 'std :: sort'?

Alterando para std::sort(begin(foo2), end(foo2))corrigir o problema.

MSVC compila o código acima conforme escrito.

Por que a diferença de tratamento entre std::vectore std::array; e qual compilador está correto?

Guy Middleton
fonte
sort(...-> std::sort(.... Eu acho que a ADL (pesquisa dependente de argumento) é o que está atrapalhando você. Isso, ou guias de dedução. Em qualquer caso; sempre qualifique as funções que você chama.
Jesper Juhl 03/01
3
Pode ser que a biblioteca MSVC tenha alguma especialização std::sortque leva à pesquisa dependente de argumento (como você já possui std::begine std::end)?
Algum programador
11
@Someprogrammerdude Simplesmente todos os contêineres no stdlib do VC ++ usam iteradores de tipo de classe definidos namespace stdmesmo onde um tipo simples de ponteiro teria funcionado. Eu acredito que isso é para inserir verificações de depuração para detectar excedentes e outros erros comuns.
François Andrieux

Respostas:

16

Isto é se resume ao tipo que begine endresultado de e como isso funciona com Argumento Dependente Lookup .

No

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

você recebe

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

e uma vez que std::vector<int>::iteratoré membro de stdachados ADL sortem stde a chamada tiver êxito.

Com

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

Você recebe

sort(int*, int*)

e como int*não é membro std, a ADL não analisará stde você não poderá encontrar std::sort.

Isso funciona no MSVC porque

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

torna-se

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

e uma vez que std::_Array_iteratorfaz parte dos stdachados da ADL sort.

Ambos os compiladores estão corretos com esse comportamento. std::vectore std::arraynão tem nenhum requisito sobre qual tipo é usado para o iterador, exceto que satisfaz o requisito LegacyRandomAccessIterator e no C ++ 17, para std::arrayque o tipo também seja um LiteralType e no C ++ 20 seja um ConstexprIterator

NathanOliver
fonte
11
Acho que a pergunta é se o comportamento do MSVC está em conformidade, ou seja, o std::arrayiterador precisa ser int*ou pode ser do tipo classe? Da mesma forma std::vector, seria relevante para a pergunta se o iterador deve ser um tipo de classe no qual a ADL funcionará ou se pode ser int*também.
noz
@ walnut Pode ser o que a implementação quiser. Poderia ser um std::iterator, algo mais, ou apenas um ponteiro.
NathanOliver
11
Também me pergunto por que, nesta implementação de biblioteca, eles escolheram usar int*para, std::arraymas não para std::vector.
François Andrieux
11
O iterador digita para ambos std::arraye std::vectornão é especificado, o que significa que a implementação pode defini-los como ponteiros brutos (o código não será compilado) ou wrappers do tipo de classe (o código será compilado apenas se o tipo de classe tiver stdum espaço de nomes associado ao ADL).
aschepler
11
Aqui está uma demonstração em que a ADL falha com um alias e aqui está uma demonstração em que a ADL obtém êxito com uma classe aninhada. Aqui e nos meus testes anteriores, std::vector<T>::iteratoré um alias.
user2357112 suporta Monica