Подтвердить что ты не робот

Почему я должен всегда указывать диапазон в функциях алгоритма STL явно, даже если я хочу работать со всем контейнером?

При использовании функций STL, таких как sort() или min_element(), мне всегда нужно указывать диапазон начинать и заканчивать явно:

void range_example()
{
    std::vector<int> list = {7, 3, 9, 1, 5, 2};
    auto found_element = std::min_element(list.begin(), list.end());
    std::cout << *found_element << std::endl;
}

Это имеет смысл, если я намереваюсь работать только на части моего контейнера, но чаще мне нужны функции для работы со всем контейнером. Есть ли причина, по которой нет перегруженной функции, которая позволяет это:

std::vector<int> list = {7, 3, 9, 1, 5, 2};
auto found_element = std::min_element(list);

Есть ли способ выполнить вызов функции для всего диапазона контейнера, который я упустил?

EDIT: Я знаю, что я могу инкапсулировать это в функцию самостоятельно, но поскольку это должно быть сделано для всех функций, я бы хотел избежать этого, если есть лучший способ.

4b9b3361

Ответ 1

В большинстве случаев стандартная библиотека предназначена для обеспечения минимального интерфейса, необходимого для выполнения всех требуемых задач, т.е. & thinsp; он пытается избежать раздувания интерфейса. Вы можете работать со всем контейнером, когда алгоритм принимает пару итераторов, но вы не можете работать на поддиапазоне, если алгоритм принял контейнер. Таким образом, пара итераторов более фундаментальна, и так, что предоставляет стандартная библиотека. Функции удобства обычно не включаются.

Однако вы, конечно, не первый человек, который так думает, и там вся библиотека Boost.Range, посвященная обработке диапазона (как контейнера, так и произвольный диапазон) как единый объект вместо пары итераторов.

Существует также официальное предложение о включении библиотеки диапазона Eric Niebler в будущую версию стандартной библиотеки С++.

Ответ 2

Это связано с тем, что алгоритмы STL не зависят от контейнера. Итераторы обеспечивают единый способ работы, при этом единственным ограничением является то, каковы гарантии, которые этот алгоритм требует от этих итераторов.

Например, если вы хотите выполнить линейный поиск min_element(), вам понадобятся только итераторы вперед (т.е. они должны поддерживать operator++). Таким образом, вы можете написать одну простую шаблонную реализацию, которая будет работать практически с каждым контейнером, несмотря на то, как контейнер реализуется под капотом.

Вы можете перегружать функции, чтобы принимать только контейнер, и применять к ним теги begin() и end(), но это будет означать, что у вас есть еще один интерфейс для запоминания.

Edit

Я предполагаю, что есть несколько других аргументов, которые можно было бы сделать. Поскольку STL была связана с математической красотой и акцентом на то, что алгоритмы отделены от контейнеров, всегда проходящие итераторы укрепили бы это понятие.

С другой стороны, в терминах языка С++ в целом одной из основных целей Stroustrup было обучение разработчиков. Полная мощность алгоритмов STL исходит из возможности пропускать произвольные диапазоны итераторов, но большую часть времени вы хотите работать на весь контейнер. Если вы предоставили перегрузки для всего контейнера, можно было бы утверждать, что большое количество людей никогда не захочет учиться использовать версии диапазона, потому что именно эти версии попадают в категорию "другой интерфейс для запоминания".

Ответ 3

Практическая причина, по которой перегрузка контейнеров или диапазонов еще не была выполнена, связана с предложением концепций.

Сейчас алгоритмы берут кучу параметров шаблона и устанавливают на них требования. Если вы передаете типы, не соответствующие требованиям, они могут не скомпилироваться или просто не работать должным образом.

Перегрузки почти всегда просто включают в себя различное количество параметров.

Если мы добавим перегрузку контейнеров/диапазонов, нам нужно либо дать им новые имена (ick), либо изменить существующие алгоритмы, чтобы быть перегруженными. Перегрузка (итератор, итератор, значение) и перегрузка (диапазон, значение, функция) имеют одинаковое количество аргументов, и один из них может легко запутать компилятор (и могут возникнуть неожиданные результаты).

Пока мы могли идти и указывать ограничения перегрузки для всех существующих алгоритмов один за другим, а затем добавлять перегрузки для диапазонов, на данный момент код и требования будут уродливыми. После добавления понятий к языку мы, как мы надеемся, будем иметь набор кратких концепций, которые описывают, какие параметры должны быть, и языковая функция, которая делает реализацию легкой и чистой.

Может оказаться, что эти алгоритмы могут не быть практически перегрузками существующих алгоритмов из-за причин совместимости или того, что у вас есть, но даже это будет легче работать.

Первоначально, итераторов было достаточно, и они отделяли контейнеры от алгоритмов. Тогда могли быть добавлены диапазоны, но языковой механизм для интерпретации контейнеров с чистым диапазоном несколько не хватался (например, тип decltype), и это не было строго обязательным. С тех пор поддержка диапазона была желательной, но сделать это нелегко, и есть (на горизонте) расширение языка, которое сделает его намного чище и проще.

Ответ 4

Вы можете реализовать свои собственные:

template<class Container>
typename Container::iterator min_element(Container& c) {
    using std::begin;
    using std::end;
    return std::min_element(begin(c), end(c));
}

std::vector<int> list = {7, 3, 9, 1, 5, 2};
auto found_element = min_element(list);

Для полноты:

template<class Container>
typename std::conditional<
  std::is_const<Container>::value,
  typename Container::const_iterator,
  typename Container::iterator>::type min_element(Container& c) {
    using std::begin;
    using std::end;
    return std::min_element(begin(c), end(c));
}

и поддерживать массивы:

template<typename T, size_t N>
T* min_element(T (&arr)[N]) { return std::min_element(arr, arr + N); }

Ответ 5

Это один из моментов, когда я думаю, что использовать макросы хорошо. Просто убедитесь, что вычисление выражения внутри макроса не имеет побочных эффектов.

#include <boost/preprocessor/punctuation/comma.hpp>
// this is just: #define BOOST_PP_COMMA() ,

#define RANGE_ARGS( container ) container.begin ( ) BOOST_PP_COMMA() container.end ( )
#define RANGE_ARGS_C( container ) container.cbegin ( ) BOOST_PP_COMMA() container.cend ( )
#define RANGE_ARGS_R( container ) container.rbegin ( ) BOOST_PP_COMMA() container.rend ( )
#define RANGE_ARGS_CR( container ) container.crbegin ( ) BOOST_PP_COMMA() container.crend ( )

Это дает в вашем случае:

std::vector<int> list = {7, 3, 9, 1, 5, 2};
auto const found_element = std::min_element( RANGE_ARGS_C(list) );

Ответ 6

Легко определить такую ​​функцию самостоятельно. Например

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

template <class T>
decltype( auto ) min_element( T &c )
{
    return std::min_element( std::begin( c ), std::end( c ) );
}

int main()
{
    int a[] = { 5, 7, 3, 1, 9, 6 };

    std::cout << *min_element( a ) << std::endl;

    std::vector<int> v( std::begin( a ), std::end( a ) );

    std::cout << *min_element( v ) << std::endl;
}    

Выход программы

1
1

Я сделал такое предложение для алгоритмов std::sort и std::reverse. Вы можете прочитать об этом на моем личном форуме, который я поддерживаю, например, в моей дорожной интернет-странице. здесь

Хотя это написано на русском языке, вы можете перевести его, например, с помощью Bing или google.