请教大虾们一个vector的问题!!!!

euphony 2003-08-29 02:49:41
为什么在泛型算法中使用vector的递增或递减iterator会出错?比如:
std::vector<int> vec;
for (int i = 0; i < 9; ++i)
{
vec.push_back(i);
}
std::vector<int>::iterator pos;
pos = find(++vec.begin(), vec.end(), 5); //出错

但是编译出错,错在++vec.begin(),《C++标准程序库》一书中说是因为C++不允许修改任何基本型别(包括指针)的暂时值,但是vector的iterator通常会实作为一般指针。如果换成list等容器就不会出错。可是我还是不理解什么叫“C++不允许修改任何基本型别的(包括指针)暂时值”?为什么会这样?哪位大虾能解释一下?万分感谢!!!
...全文
112 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
短歌如风 2003-09-04
  • 打赏
  • 举报
回复

对不起,看错了。《C++ Standard Library》说的是对的——我有看到“基本”二字。
短歌如风 2003-09-04
  • 打赏
  • 举报
回复

楼上所举的例子并非是“临时变量”,它们是参数,这和临时变量是不同的。

不过临时变量确实不一定是const的。在作为返回值时的临时变量来讲,简单类型者不是左值,把它看成是const类型也是可以的。而对象类型的返回值是否可以调用非const成员函数取决于是const类型还是非const类型,可见它并不是天生就是const的。“C++不允许修改任何基本型别(包括指针)的暂时值”这句话应该有它上下文,否则就是错误的。对象类型的暂时值是可以修改的——调用对象的方法或自定义操作符。COW语义的对象通常都用这种方法判断用户调用一个成员是为了取值还是为了修改。

“list等容器就不会出错”的原因我已经解释过了,因为它们返回的iterator不是指针而是一个复杂对象类型,只要它定义了前缀++操作符并且返回值不是const类型(或者前缀++操作符有const属性)就可以通过编译;而有些STL的实现中vector的iterator其实是一个指针,指针类型返回值是不被认为左值的,不能使用前缀++操作符进行操作,因此无法通过编译。而楼主所说的几个用vector通过编译的例子我觉得不是因为编译器的原因而是因为的实现的原因,它们的vector::iterator应该不是一个指针类型。
THEBEST 2003-09-04
  • 打赏
  • 举报
回复
"指针类型返回值是不被认为左值的"

为什么呢?
THEBEST 2003-09-04
  • 打赏
  • 举报
回复
为什么:pos = find(++vec.begin(), vec.end(), 5); //出错

应该看成
vector::const_iterator ptemp = vec.begin();
pos = find(++ptemp, vec.end, 5);

什么时候什么情况下会产生临时对象呢?
这里它为什么会产生一个临时对象呢?
短歌如风 2003-09-04
  • 打赏
  • 举报
回复

简单的解决办法是:写一个模板函数来处理:

template <typename T>
inline T& next_iterator(T& i)
{
return ++ i;
}

然后用nect_iterator(coll.begin())代替++coll.begin()。
短歌如风 2003-09-04
  • 打赏
  • 举报
回复

简单类型返回值不允许修改是一个规定,大家只要遵循就行了。

至于为什么这样规定,我想是为了编译器方便。因为简单类型返回值的处理和复杂类型是不同的,复杂类型通常是由调用者在栈中分配一个临时变量,把它的地址作为一个参数传给函数,函数把返回值写到这个变量中。也就是说,当返回值是复杂类型时,它的实现相当于是用一个“变参”来返回结果。而简单类型则不同,由于它们长度通常都比较小,可以通过寄存器(或浮点寄存器)来返回。这样就可以看出区别:复杂类型的返回值有一个地址,是真正的变量,而简单类型不是。

同时,允许简单类型的返回值作为左值是没有意义的——对简单类型的修改操作都只会影响它自身,而修改完后它就不存在了(至少是不能访问了,因为它没有名字)。而复杂类型不同,它的操作符都是自定义的,其操作意义不只影响自身——它可能是一个对引用语义的封装。语言从没有考虑到你会只为了取得操作的结果而不需要它的副作用而使用一个要求左值的操作符。也就是说,你用++操作符时其实只想得到它+1的值而并不关心此后这个变量本身是什么值。事实上仅对于指针来说这还是没有意义的——这时你应该用+1而不应该用++。不过当我们的代码可以用于任何迭代器时就有了一个问题——某些迭代器没有operator+。
THEBEST 2003-09-03
  • 打赏
  • 举报
回复
TO: 楼主:
为什么说“如果换成list等容器就不会出错。”这是什么原因呢?

为什么:pos = find(++vec.begin(), vec.end(), 5); //出错

应该看成
vector::const_iterator ptemp = vec.begin();
pos = find(++ptemp, vec.end, 5);

什么时候什么情况下会产生临时对象呢?
这里它为什么会产生一个临时对象呢?


还有:“临时变量一定是一个const量” // 我认为是错误的。如下:
比如:
swap (int a ,int b) // 试图去交换a ,b 存储的値。故意这样的。为了按值传递。
{
int temp = a;
a = b;
b = temp;
}
调用 :
int a = 4, b = 3;
swap ( a, b );
我们知道调用时会产生临时对象用来存放实参的COPY。因为是按值传递的是吧。
而后执行函数体,交换两个值,照你说临时对象都是const 的,那这里产生的两
个临时对象不也是const 那还不能改变它们的值?
但我们都知道这个函数调用的结果是把这两个临时对象的值交换了,而没有影响
实参的值。不是吗?
短歌如风 2003-09-01
  • 打赏
  • 举报
回复
补充一句:
正是由于上述原因,如果你的容器不是vector而是deque,则这段代码是可以通过编译的——deque的复杂性使它的iterator不能是一个简单的指针。
短歌如风 2003-09-01
  • 打赏
  • 举报
回复

这个编译错误并不因为返回值为const,而是与库的实现有关。前缀++操作符要求参数是个左值,而简单类型返回值都不是左值。如果vector::iterator定义为一个指针(属于简单类型)则无法通过编译,而如果是一个定义了前缀++操作符的对象则可以。为了代码不依赖于库的实现,应该使用一个临时变量。事实上,一般的实现中vector::iterator都是用一个简单的typedef定义为类型——出于性能的考虑。

此外,简单类型的返回值不是左值并且不能使用前缀++等要求左值的操作符是符合标准的。

如下所示:
int * test()
{
return 0;
}

...
++ test();//错误!

class MyPtr
{
public:
MyPtr& operator ++()
{
return *this;
}
};

MyPtr test2()
{
return MyPtr();
}

...
++test2();//正确。
DancingCalf 2003-09-01
  • 打赏
  • 举报
回复
该代码在vc6通不过,vc7通过,是编译器改善的结果。
vc6下只好变通屈就了。
jyfcsdn所写方法可行。
jyfcsdn 2003-09-01
  • 打赏
  • 举报
回复
请理解这句话具体做了哪些事
pos = find(++vec.begin(), vec.end(), 5); //出错

应该看成
vector::const_iterator ptemp = vec.begin();
pos = find(++ptemp, vec.end, 5);

++ptemp是出错的原因,不管vec.begin(),调用的是
iterator begin() { return _M_start; }还是
const_iterator begin() const { return _M_start; }
euphony 2003-08-30
  • 打赏
  • 举报
回复
GNU的编译器应该更贴近标准c++,但是标准c++好像又是不允许上面程序通过,这是怎么回事
euphony 2003-08-30
  • 打赏
  • 举报
回复
刚才用bcb7编译了一下,发现只有GNU的g++可以通过,bcc和microsoft的都不行。
leechildren 2003-08-30
  • 打赏
  • 举报
回复
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{

std::vector<int> vec;
for (int i = 0; i < 9; ++i)
{
vec.push_back(i);
}
std::vector<int>::iterator pos;
pos = find(++vec.begin(), vec.end(), 5);
cout << *pos << endl;
}

我刚才在.net 环境下运行, 没有问题。
TopCat 2003-08-29
  • 打赏
  • 举报
回复
to rtdb(东临碣石):

不一定的,要看这个iterator的类型,只有random iterator才能这样用。
rtdb 2003-08-29
  • 打赏
  • 举报
回复
罗嗦一句, 这样是好用的:
pos = find(vec.begin()+1, vec.end(), 5);

TopCat 2003-08-29
  • 打赏
  • 举报
回复
所谓临时对象,就是没有任何显式的标识符可以引用到该变量的变量,这种变量随时可能被销毁。

比如:
class C; //有一个类C,其中含有一个函数func
C().func(); //产生一个临时变量,并调用此临时变量的成员函数func。

这里,这个C()确实产生了一个对象,但是没有任何方法在其他地方使用这个变量,这就是所谓的临时变量,此外,函数调用时,如果参数不是指针或引用,也会产生一个临时变量并同时调用其拷贝构造函数,将实参传入函数内部。

临时变量一定是一个const量。就拿上例来说,这个用法类似于以下写法:

const C inst; //注意这里的const
inst.func();

只不过我们无法存取这个“inst”而已。

所以,你在find函数调用时,所产生的临时变量也是一个const量,所以说,它的返回值一定是const_iterator,所以你对它++会出错。
euphony 2003-08-29
  • 打赏
  • 举报
回复
哦,谢谢,能不能解释一下临时对象的含义?
liu_feng_fly 2003-08-29
  • 打赏
  • 举报
回复
c++标准就是这样规定的,比如int()会产生一个临时对象,但是这样++int(),在产生的临时对象上++就不允许,我认为这样做没有意义:)而对于用户自定义的class,产生一个临时对象并且++是否有意义呢?这完全由写这个类的程序员控制了,所以就允许了,我是这样理解的。
简单的说,编译通不过是因为c++标准的规定
euphony 2003-08-29
  • 打赏
  • 举报
回复
但是vector的begin()有两种返回类型啊

public:
iterator begin() { return _M_start; }
const_iterator begin() const { return _M_start; }


加载更多回复(1)

24,855

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 工具平台和程序库
社区管理员
  • 工具平台和程序库社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧