为什么std::basic_string::substr不遵循[begin,end)约定?

3

方法std::basic_string::substr有参数pos, count,指定逻辑范围内的元素位置[pos, pos + count + 1)。相反,大多数标准库函数(例如std::for_each)具有类似于begin, end的某种形式的参数,指定范围[begin, end)

这似乎是一个异常情况,对某些人来说很困惑(请参见此处此处此处此处)。为什么这里没有使用通常的范围约定?还要注意,另一个随机访问容器的方法std::vector::erase遵循了通常的约定。

2
您可以通过使用构造函数来获得相同的行为。例如:std::string substring{text.begin(), text.end()}。不知道为什么 substr 没有这样的重载。 - Zereges
@Zereges:因为这是多余的。如果您有两个迭代器表示子字符串,为什么要将它们传递给std::string::substr()重载,而不是也可以将它们传递给std::string::string() - MSalters
@MSalters STL 中存在许多冗余的重载甚至函数。STL 的设计并不是为了最简化。 - Zereges
3个回答

6

历史原因。标准库有多个起源之一是STL。它引入了begin,end的约定。std::string早于STL合并到标准库,已经有大量现有代码使用.substr(pos,length)


我认为如果我们现在设计这个库,std::string看起来会非常不同。 - Martin Bonner supports Monica

5

一个简单的猜测:

你提到的不同方法有不同的行为,这可能是为什么有些使用迭代器而有些不使用的原因。

std::for_each是通用的 - 在任何容器(甚至是原始数组)上工作的通用方法最简单的方法就是使用迭代器。

std::vector::erase序列容器概念的一部分,因此它必须具有可以在任何类型的容器上工作的“通用”形式(您可以为std::vector使用poscount,但对于std::liststd::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 都是定义明确的。另一方面,std::basic_string::substr仅作为std::basic_string 的一部分存在(与erase不同,erasestd::vectorstd::list等的一部分),并返回一个std::basic_string1 (不是迭代器,在这里你会用迭代器做什么?)。
std::basic_string中有其他“非通用”(即没有一些概念的强制要求)方法,通常是整个find系列方法、insert带有size_type的重载版本、append等等。
从主观角度看,我认为最好有一个std::basic_string,它的行为不能像其他容器那样,因为它不是(我不确定标准是否要求std::basic_string是一个SequenceContainer 或类似物)。 1 在这里不能返回迭代器,因为你需要一个新的std::basic_string,所以你将拥有一个接受迭代器但返回对象的方法...... 我觉得这比使用pos/count而不是first/last更令人不安。

1
我不知道为什么,我没有在那里,但是你错过了一个针对 [begin, end) 约定的范围构造函数。
template< class InputIt >
basic_string( InputIt first, InputIt last,
              const Allocator& alloc = Allocator() );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接