Неквалифицированный sort () - почему он компилируется при использовании на std :: vector, а не на std :: array, и какой компилятор корректен?

11

При звонке 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, и clang возвращают ошибку в сортировке std::array- clang говорит

ошибка: использование необъявленного идентификатора 'sort'; Вы имели в виду 'std :: sort'?

Изменение, чтобы std::sort(begin(foo2), end(foo2))исправить проблему.

MSVC компилирует код выше, как написано.

Почему разница в лечении между std::vectorа std::array; а какой компилятор правильный?

Гай Миддлтон
источник
sort(...-> std::sort(.... Я предполагаю, что ADL (поиск, зависящий от аргумента) - это то, что сбивает вас с толку. Это, или руководство по выводу. В любом слючае; всегда уточняйте функции, которые вы вызываете.
Джеспер Юл
3
Может быть, у библиотеки MSVC есть некоторая специализация, std::sortкоторая приводит к зависимому от аргумента поиску (как у вас уже есть для std::beginи std::end)?
Какой-то программист чувак
1
@Someprogrammerdude Это просто, что все контейнеры в stdlib VC ++ используют итераторы типа класса, определенные namespace stdдаже там, где работал бы простой тип указателя. Я полагаю, что это вставка проверок отладочной сборки для обнаружения переполнений и других распространенных ошибок.
Франсуа Андриё

Ответы:

16

Это сводится к типу, который beginи endрезультат и как это работает с Argument Dependent Lookup .

В

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

ты получаешь

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

и так std::vector<int>::iteratorкак член stdADL находит sortв stdи вызов успешно.

С

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

Ты получаешь

sort(int*, int*)

и потому что int*не является членом std, ADL не будет смотреть, stdи вы не можете найти std::sort.

Это работает в MSVC, потому что

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

становится

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

и так как std::_Array_iteratorявляется частью stdADL находок sort.

Оба компилятора верны с таким поведением. std::vectorи std::arrayне предъявляют никаких требований к тому, какой тип используется для итератора, за исключением того, что он удовлетворяет требованию LegacyRandomAccessIterator, а в C ++ 17 для std::arrayэтого типа также должен быть LiteralType, а в C ++ 20 - в качестве ConstexprIterator.

NathanOliver
источник
1
Я предполагаю, что вопрос в том, соответствует ли поведение MSVC, то есть должен ли быть std::arrayитератор или это может быть тип класса? Точно так же было бы уместно ответить на вопрос, должен ли итератор быть типом класса, на котором будет работать ADL, или это также может быть . int*std::vectorint*
грецкий орех
@walnut Может быть так, как хочет реализация. Это может быть std::iteratorчто-то еще или просто указатель.
Натан Оливер
1
Мне также интересно, почему в этой реализации библиотеки они решили использовать int*для, std::arrayно не для std::vector.
Франсуа Андриё
1
Типы итераторов для обоего std::arrayи std::vectorне определены, то есть реализации допускаются определять их в качестве сырья указателей (код не скомпилируется) или обертки класса типа (код скомпилируется , только если тип класса имеет stdкак ADL связанное пространство имена).
Ашеплер
1
Вот демонстрация, где ADL завершается неудачно с псевдонимом, и вот демонстрация, где ADL завершается успешно с вложенным классом. И здесь, и в моих предыдущих тестах, std::vector<T>::iteratorэто псевдоним.
user2357112 поддерживает Monica