2016-09-28 3 views
2

Метод std::basic_string::substr имеет аргументы pos, count, указав логический диапазон элементов в позициях [pos, pos + count + 1). С другой стороны, большинство стандартных библиотечных функций (например, std::for_each) имеют аргументы, в той или иной форме, подобной begin, end, указав диапазон [начало, конец)Почему не std :: basic_string :: substr следует за соглашением [начало, конец]?

Это, кажется, исключение, то, сбивает с толку некоторых (см. вопросы here, here, here и here). Почему здесь не было обычного диапазона? Также обратите внимание, что std::vector::erase, метод другого контейнера с произвольным доступом, делает, следуя обычному соглашению.

+2

Вы можете получить такое же поведение, используя contrustor. Например. 'std :: string substring {text.begin(), text.end()}'. Не знаю, почему 'substr' не имеет такой перегрузки. – Zereges

+0

@Zereges: Потому что это избыточно. Если у вас есть два итератора, обозначающих подстроку, зачем передавать их в перегрузку 'std :: string :: substr()', если вы также можете передать их в 'std :: string :: string()'. – MSalters

+0

@MSalters В STL много избыточных перегрузок или даже функций. STL не является минималистичным. – Zereges

ответ

5

Исторические причины. Стандартная библиотека имеет несколько истоков, одним из которых является STL. Это принесло соглашение begin,end. std::string предшествует слиянию STL в стандартную библиотеку, и было много существующего кода, который использовал .substr(pos,length).

+0

... и я думаю, что если бы мы сейчас разрабатывали библиотеку, 'std :: string' выглядел бы совсем по-другому. –

5

Простое предположение:

различные методы, которые вы процитировать имеют различное поведение, которое, вероятно, почему некоторые используют итераторы и другие не делают.

std::for_each является общим - самый простой способ иметь универсальную версию метода, который работает на любых контейнерах (и даже на необработанном массиве), - использовать итераторы.

std::vector::erase является частью SequenceContainer концепции, поэтому он должен иметь «общий» вид, который может работать на любом виде контейнера (можно использовать pos и count для std::vector, но что о std::list? Или в std::set?). Имея такое понятие полезно создать универсальный код:

template <typename C> 
void real_remove(C &c, const typename C::value_type &value) { 
    c.erase(std::remove(c.begin(), c.end(), value), c.end()); 
} 

Это работает только потому, что std::...::erase хорошо определены для любого SequenceContainer.

С другой стороны, std::basic_string::substr только часть std::basic_string (в отличии от erase, которая является частью std::vector, std::list, ...) и возвращает std::basic_string (не итератор, что бы вы сделали с итератором здесь ?).

Есть другие «нетипичная» (то есть, что не являются обязательными некоторыми понятиями) метода в std::basic_string, как правило, все семейство find методов, insert имеет перегрузку с size_type, append, и так далее.

На субъективный взгляд, я считаю, что лучше иметь std::basic_string, не ведут себя, как и другие контейнеры, потому что это не так (я не уверен, что стандарт требует std::basic_string быть SequenceContainer или что-нибудь, так а).

Вы не можете вернуть итераторы здесь, потому что вы хотите новый std::basic_string, так что вы бы метод принятия итераторов, но возвращающий объект ... Я нашел бы это гораздо большее беспокойство, чем с pos/count вместо first/last.

1

Я не мог сказать, почему меня там не было, но есть конструктор диапазона для соглашения [начало, конец], которое вы пропустили.

template< class InputIt > 
basic_string(InputIt first, InputIt last, 
       const Allocator& alloc = Allocator());