C++中关于函数返回用户自定义类型是左值还是右值?

demephy 2006-12-11 08:42:43
今天在VC中写了如下一个测试程序,发现如果函数返回类型为用户自定义类型,
那么返回值可以作为左值被修改,但是内置类型好象不行,而我感觉返回值应该是右值.到底是我错了还是编译器问题?好象在.net中下面同样可以编译并正常运行~
struct st{
int a;
int b;
int c;
int d;
st(){}
st(const st& stst){a=stst.a;b=stst.b;c=stst.c;d=stst.d;}
};
st func(st k)
{
k.a = 0;
return k;
}
int main()
{
st aa,bb;
func(aa)=bb;
return 0;
}
...全文
1438 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
blue_zyb 2006-12-12
  • 打赏
  • 举报
回复
函数的返回值是“右值”,这是标准上明确规定的。
---------------------
原文
OOPhaisky 2006-12-12
  • 打赏
  • 举报
回复
哈哈,楼主的问题已经是“老生常谈”了。
函数的返回值是“右值”,这是标准上明确规定的。
但是,对于返回内建类型和返回自定义类型,还是有区别的:
返回内建类型,返回值不能被修改(这正是“右值”的体现);
返回自定义类型,返回值可以被修改,这违反了“右值”的含义,但是标准说它又是一个“右值”,怎么办呢?那就把这个返回值成为“可以修改的右值”吧!(可以修改的右值,的确是一个自相矛盾的字眼儿,但是没办法,现实是残酷的)
taodm 2006-12-12
  • 打赏
  • 举报
回复
汗,我们又来讨论左右值了。我上面发言已完,闪人。
blue_zyb 2006-12-12
  • 打赏
  • 举报
回复
按C++标准,只有返回引用的才是左值;返回指针的,解引用后可以变成左值(当然这是另一回事)。一般的不可以,所以,在这一点上,VC没有严格按照标准来。
-------------------------------------
代码在mingw下的g++下编译也没有问题
并且,我看thinking in C++中的chapter 8 constants中讲解这一问题时说到:
Only the non-const return value can be used as an lvalue.
Thus, it’s important to use const when returning an object by value
if you want to prevent its use as an lvalue.

当然,我更相信标准,你说的“一般的不可以”的原文出处在哪里?
taodm 2006-12-12
  • 打赏
  • 举报
回复
C++有明确规定的,不过,你得翻C++标准了。
大致就是函数返回的是“无名临时变量”,不可以绑定给非const引用,但仍然可以被修改。
因为C++里,用户自定义类型是一等公民。
demephy 2006-12-12
  • 打赏
  • 举报
回复
我仔细查看了一下vc编译后的汇编代码,发现如果函数func返回类对象,那么会在调用者函数main的栈帧中预留这个对象所需要的空间,然后在调用函数的时候,在把参数逆序压栈后,紧接着把预留的返回对象的地址压栈,然后调用函数func运行。函数func运行过程中,实际上是通过[ebp+8]取得把返回对象的地址后,对这个地址空间进行调用构造或者拷贝构造函数构造返回对象,然后再把这个地址写入eax进行返回。
从汇编代码上来看,函数的返回值是在调用者caller的栈帧中进行分配的,函数返回后并不会随着被调用者callee函数的退栈而丢失,这个地址空间会和caller的作用域相同,
jixingzhong(瞌睡虫•星辰) 大哥说它不是左值,原因是“左值必须有相应的内存空间~~”
但是上面的结论说明它不断有内存空间而且作用域也和caller作用域相同。
如果函数返回内置类型,而且返回值的sizeof小于4byte,那么返回值一般直接写入eax寄存器,大于4byte小于8byte直接放在eax和edx中返回,这种返回值确实没有内存空间,不可以做左值。
分析完汇编代码突然感觉好象vc的做法有它的道理,感觉htqx(航天奇侠)大哥说的“自定义对象本身不能说是值吧。一般的值,只是相对内置类型来说。”有点道理,但是还是没有能找到权威的C++语法规定支持它,哪位大哥能帮忙解决一下,有C++语法规定支持它吗?
lauxp 2006-12-12
  • 打赏
  • 举报
回复
T func()
{
T t;
return t;
}

// pseudo code after compile
// remangle func.
void func(&T __t)
{
T tt;
tt.T::T();
__t.T::T(tt);
return;
}

func() = k; 可能直接被编译成
T t;
func(t);
t.operator=(k);

明天去公司在g++下面confirm一下,家里没有vc的编译环境.

至于左值右值的问题,我觉得多半会有点过时,希望有谁出来介绍澄清一下,学习。
whatabig 2006-12-12
  • 打赏
  • 举报
回复
const st& func(st& k)
{
k.a = 0;

return k;
}
这样实现,限定func()不能做Lvalue。
taodm 2006-12-12
  • 打赏
  • 举报
回复
这个问题不简单,无法简单解释。
demephy 2006-12-12
  • 打赏
  • 举报
回复
没有那本书呀,也没有电子版的.
能不能简单解释一下~
taodm 2006-12-12
  • 打赏
  • 举报
回复
a=b=c等价于(a=b)=c;”就是从effective C++这个item讹传出来的。

楼主的最后问题,去看<深度探索C++对象模型>
demephy 2006-12-12
  • 打赏
  • 举报
回复
关于函数返回值,我还有一个帖子:
http://community.csdn.net/Expert/topic/5103/5103360.xml?temp=.6374933
(关于C++和C在函数返回值为复杂结构体变量上的实现差别)
虽然已经结帖,但是还是有点糊里糊涂,很多人的回答和我问的不是一个东西.
函数返回的是无构造函数的类对象,那么会在caller的栈帧中预留两个sizeof(返回对象)的空间,
在callee函数返回后,copy两次,这个至今还是有点不明白,我看今天人多,各位大哥,能否给出权威的解答吗?
0黄瓜0 2006-12-12
  • 打赏
  • 举报
回复
这段话的意思是说operator=不应返回const引用,而应返回引用对吧
taodm 2006-12-12
  • 打赏
  • 举报
回复
“a=b=c;//假设a,b,c都是用户自定义的类,并且重载了=操作符
然后解释说上面语句等价于(a=b)=c;”
谁说的?打他屁股。
Effective C++ item 15 原话是
另一个常犯的错误是让operator=返回一个const对象的引用,象下面这样:
class Widget {
public:
...
const Widget& operator=(const Widget& rhs);
...
};
这样做通常是为了防止程序中做象下面这样愚蠢的操作:
Widget w1, w2, w3;
...
(w1 = w2) = w3; // w2赋给w1, 然后w3赋给其结果
// (给operator=一个const返回值
// 就使这个语句不能通过编译)
这可能是很愚蠢,但固定类型这么做并不愚蠢:
int i1, i2, i3;
...
(i1 = i2) = i3; // 合法! i2赋给i1
// 然后i3赋给i1!
这样的做法实际中很少看到,但它对int来说是可以的,对我和我的类来说也可以。那它对你和你的类也应该可以。为什么要无缘无故地和固定类型的常规做法不兼容呢?
demephy 2006-12-12
  • 打赏
  • 举报
回复
demephy(襄王有梦),按标准是禁止,但比较老的编译器是允许,不过至少要给warning。连warning都不给的编译器是必须要扔掉了。
_____________________________________________________________
我刚才试了,.net上也是这样,
st &cc = func(bb);同样编译成功,
不知道ms为什么在新的版本编译器上还要保留这个标准禁止的语法?

另外,再问一下关于=操作符重载,
经常看到有人写
a=b=c;//假设a,b,c都是用户自定义的类,并且重载了=操作符
然后解释说上面语句等价于(a=b)=c;
我却认为是a=(b=c);
难道运算符重载把赋值运算符右结合的属性也改了吗?
blue_zyb 2006-12-12
  • 打赏
  • 举报
回复
哈哈,ls所言从实际应用的角度来说很对。

但大家不就是在考究一个语法上的技术问题么...
(虽然这并不一定有多大的实用价值,但没有实用价值的技术问题就不钻研不是我的学习哲学.
况且这些概念上的东西极有可能帮你打通“七经八脉",呵呵)
0黄瓜0 2006-12-12
  • 打赏
  • 举报
回复
函数的返回值在逻辑上是右值.

就算在某些编译器上可以引用函数的返回值或修改函数的返回值.但是,如果知道返回值出了函数调用那条语句就析构了(也就是说函数的返回值是个临时变量),以后的引用都是非法的了,那么,你一定不会那么做.
jihailong 2006-12-12
  • 打赏
  • 举报
回复
标准是标准
市场归市场,但是这种做法不安全
wellsnow2002 2006-12-12
  • 打赏
  • 举报
回复
看不懂!学习!
taodm 2006-12-12
  • 打赏
  • 举报
回复
demephy(襄王有梦),按标准是禁止,但比较老的编译器是允许,不过至少要给warning。连warning都不给的编译器是必须要扔掉了。
加载更多回复(10)

65,211

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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