MSVC中std :: string :: operator []的奇怪行为

时间:2010-12-14 17:00:50

标签: c++ visual-studio-2010

我一直在使用一些半迭代器来标记一个std :: string,我遇到了一个运算符[]的奇怪问题。当使用char *从一个位置构造一个新字符串时,我使用了类似下面的内容:

t.begin = i;
t.end = i + 1;
t.contents = std::string(&arg.second[t.begin], &arg.second[t.end]);

其中arg.second是std :: string。但是,如果我是最后一个字符的位置,那么arg.second[t.end]将抛出一个调试断言 - 即使采用一个过去一端的指针是明确定义的行为,甚至对于原始数组也是常见的,并且因为使用迭代器调用构造函数我知道结束迭代器永远不会被引用。 arg.second[arg.second.size()]应该是一个有效的表达式,产生等同于arg.second.end()的char *似乎不合逻辑吗?

5 个答案:

答案 0 :(得分:4)

你没有指向一个结尾的指针,你在结束时访问一个,然后得到那个地址。完全不同,虽然前者定义明确,形成良好,后者也不是。我建议使用迭代器构造函数,它基本上是你正在使用的,但使用迭代器而不是char *。见亚历山大的评论。

答案 1 :(得分:3)

operator[](size_type pos) const不会返回一个过去的结尾pos == size();它返回charT(),这是一个临时的。在const的非operator[]版本中,行为未定义。

21.3.4 / 1

  

const_reference运算符[](size_type pos)const;   reference operator [](size_type pos);   1返回:如果pos< size(),返回data()[pos]。否则,如果pos == size(),则为const   version返回charT()。否则,行为未定义。

答案 2 :(得分:2)

明确定义的是在结束时创建一个 迭代器 。 (指针也可能是迭代器。)但是,解除引用这样的迭代器将产生未定义的行为

现在,您正在做的是 数组订阅 ,这与形成迭代器非常不同,因为它返回对引用对象的引用(很多)类似于解除引用迭代器)。您肯定 来访问一个接一个的数组。

答案 3 :(得分:1)

std::string不是数组。它是一个对象,其接口松散地类似于一个数组(即提供operator[])。但那是相似性结束的时候。

即使我们暂时假设std::string只是一个构建在普通数组之上的包装器,那么为了获得存储序列的一个接一个的指针,你必须做&arg.second[0] + t.end之类的事情,即不是通过std::string接口,而是先进入普通指针域并使用普通的低级指针算法。

然而,即使这种假设也是不正确的,做&arg.second[0] + t.end这样的事情也是一种灾难。 std::string不保证将其受控序列存储为数组。它不能保证连续存储,这意味着无论指针指向何处,您都不能假设您可以使用指针算法从一个迭代到另一个。

如果你想在一些传统的基于指针的界面中使用std::string,你唯一的选择是通过std::string::c_str()方法,这将生成一个非永久的基于数组的接口副本受控序列。

P.S。请注意,顺便说一下,在原始的C和C ++规范中,使用&a[N]方法获取即使是普通的内置数组的一个接一个的指针也是非法的。您始终必须确保未将[]运算符与过去的索引一起使用。获取指针的合法方式始终类似于a + N&a[0] + N,但不是&a[N]。最近的更改也使&a[N]方法合法化,但最初它不合法。

答案 4 :(得分:-1)

字符串不是原始数组,所以我想如果你正在做一些危险的事情,比如访问其范围之外的元素,那么实现可以自由添加一些调试诊断。我猜想发布版本可能会有效。

但是...

对于您要做的事情,为什么不使用basic_string( const basic_string& str, size_type index, size_type length );构造函数来创建子字符串?