函数返回临时对象的问题

dinko321 2013-06-20 10:13:11
如下一段代码:

class A;

A func()
{
A a;
return a;
}

int main()
{
A *a = &func(); // s1
A &a = func(); // s2
return 0;
}


一、
在我之前的理解中,函数func返回值,是在调用处的作用域生成的一个临时值,所以对其取地址没错(s1)。
s2同理。
但是昨天,又遇到另外个人,说这个写法不对。
测试之后发现,VS2010无问题,codeBlocks编译不过,说指针指向了临时变量。codeBlocks应该是GCC的。

二、
但是如果用容器,诸如vector之类的,就又不会报错。
我之前的理解中,我一直觉得容器返回的对象,应该就是func中的形式。


求解个中原委。
...全文
824 41 打赏 收藏 转发到动态 举报
写回复
用AI写文章
41 条回复
切换为时间正序
请发表友善的回复…
发表回复
lm_whales 2013-06-28
  • 打赏
  • 举报
回复
引用 40 楼 u010936098 的回复:
[quote=引用 39 楼 lm_whales 的回复:] 函数返回一个指针是一个正常的行为,所以编译器不报错,是正常的。 至于返回自动变量的地址还是,动态分配的地址,静态变量的地址,全局变量的地址; 这个是很不容易控制的,是用户行为,不是编译器的责任!!!!!
楼上仔细看代码了吗?楼主的函数返回值并不是指针,而是值对象。 函数的返回值是右值,右值不允许应用取地址操作符,也不能绑定到非常量引用,编译器允许这种行为是不正确的。[/quote] 抱歉,这个确实没在意!! 这个其实是标准问题,临时变量也是有地址的,允不允许取地址是标准的问题,和编译器对标准的执行问题!!! 1)标准不允许,编译器如实执行标准,这就是个错误;编译器会如实汇报错误的!! 这种情况是皆大欢喜,永远不会出问题; 或者代码总是不能编译通过,很郁闷!! 2)标准不允许,编译器没有如实执行标准( 这是编译器的BUG,或者编译器落后于标准,编译器拒不执行标准) 这种代码就是不可移植的代码,编译器不报错; 程序执行结果可能会错,这是编程者的责任,你应该保证你的程序不会出现,莫明奇妙的错误!! 3)如果标准没规定,编译器可以任意处理,错误的责任还是编程者!!! 你不应该编写,这种不能确定正确性的程序!!!
橡木疙瘩 2013-06-28
  • 打赏
  • 举报
回复
引用 39 楼 lm_whales 的回复:
函数返回一个指针是一个正常的行为,所以编译器不报错,是正常的。 至于返回自动变量的地址还是,动态分配的地址,静态变量的地址,全局变量的地址; 这个是很不容易控制的,是用户行为,不是编译器的责任!!!!!
楼上仔细看代码了吗?楼主的函数返回值并不是指针,而是值对象。 函数的返回值是右值,右值不允许应用取地址操作符,也不能绑定到非常量引用,编译器允许这种行为是不正确的。
lm_whales 2013-06-28
  • 打赏
  • 举报
回复
函数返回一个指针是一个正常的行为,所以编译器不报错,是正常的。 至于返回自动变量的地址还是,动态分配的地址,静态变量的地址,全局变量的地址; 这个是很不容易控制的,是用户行为,不是编译器的责任!!!!!
短歌如风 2013-06-26
  • 打赏
  • 举报
回复
VC死不悔改地继续让函数返回值可以被non-const reference,或许是因为MFC中有这样的代码,要知道MS早已经放弃发民MFC了。里面还有很多VC6时代的代码。 右值引用是&&,与本题无关。它是为了实现大家盼望多年的“move constructor”而提出来的,其它情况要谨慎使用。 值拷贝类型的函数返回值是右值,不能取地址,这一点毋庸置疑。 yudahai109 用调用和赋值来证明它可以取地址是错误的。虽然调用成员函数需要一个this指针,但那是实现而不是语法逻辑;仅管“左值”与“右值”的提法来源于赋值操作符,但C++的操作符重载已经让赋值操作符失去了判断 左右值的作用。如果可以对int类型的返回值赋值才能证明它是左值。判断一个表达式是否左值,只要对它使用取地址操作符就可以了。
我看你有戏 2013-06-26
  • 打赏
  • 举报
回复
引用 楼主 dinko321 的回复:
如下一段代码:

class A;

A func()
{
    A a;
    return a;
}

int main()
{
    A *a = &func();         // s1
    A &a = func();          // s2
    return 0;
}
一、 在我之前的理解中,函数func返回值,是在调用处的作用域生成的一个临时值,所以对其取地址没错(s1)。 s2同理。 但是昨天,又遇到另外个人,说这个写法不对。 测试之后发现,VS2010无问题,codeBlocks编译不过,说指针指向了临时变量。codeBlocks应该是GCC的。 二、 但是如果用容器,诸如vector之类的,就又不会报错。 我之前的理解中,我一直觉得容器返回的对象,应该就是func中的形式。 求解个中原委。

#include <iostream>
using namespace std;

#include "version.h"

int g_obj = 0;
class A
{
public:
	A()
	{
		p = new char[20];
		g_obj++;
		cout<<"A() g_obj="<<g_obj<<endl;
	}
	~A()
	{
		delete[] p;
		g_obj--;
		cout<<"~A() g_obj="<<g_obj<<endl;
	}

	char*p;
};

A func()
{
	A a;
	return a;
}

int main()
{
	{
		A *a1 = &func();         // s1
		A &a2 = func();          // s2
	}
	return 0;
}
以上代码会报错,说明lz的这种调用方式存在的大问题 试试
lm_whales 2013-06-26
  • 打赏
  • 举报
回复
这个是逻辑错误,又不是语法错误; 什么时候,编译器紧紧跟随标准,标准能够解决逻辑错误; 这种错误,才不能编译通过; 如果编译能通过的程序,都没有任何问题; 也就不需要代码测试了,debug也就不需要了!!! 你可以利用标准,和编译器,写出尽量健壮的代码; 永远也不可能利用编译器,写出永远也不会错误的代码。
allenltiverson 2013-06-22
  • 打赏
  • 举报
回复
引用 4 楼 dinko321 的回复:
[quote=引用 1 楼 rocktyt2 的回复:] func返回值是右值 不能对右值进行取地址操作,不能对右值进行引用 vector中存放的是值,容器中取元素返回的都是引用,而不是值
vs2010能过是为什么。。。[/quote] 能不能编译通过那倒不重要,但至少会有警告,这种问题本质在于C++的基础知识,别去过多的追究编译器的问题
疯狂的红豆 2013-06-20
  • 打赏
  • 举报
回复
LZ函数中返回的也不是临时对象而是局部对象,假如是return A();这样子就会返回一个临时对象,也许编译器会对临时对象优化,大多数编译器都会做这个优化的,effective C++中的条款。例如A aa = func();这样子函数的返回值就不会有构造和析构成本了,而是直接在aa对象空间中进行初始化操作。
www_adintr_com 2013-06-20
  • 打赏
  • 举报
回复
能过才更可怕
ri_aje 2013-06-20
  • 打赏
  • 举报
回复
引用 4 楼 dinko321 的回复:
[quote=引用 1 楼 rocktyt2 的回复:] func返回值是右值 不能对右值进行取地址操作,不能对右值进行引用 vector中存放的是值,容器中取元素返回的都是引用,而不是值
vs2010能过是为什么。。。[/quote] 对标准执行的不够严格,MS 总干这种事儿。
dinko321 2013-06-20
  • 打赏
  • 举报
回复
引用 1 楼 rocktyt2 的回复:
func返回值是右值 不能对右值进行取地址操作,不能对右值进行引用 vector中存放的是值,容器中取元素返回的都是引用,而不是值
vs2010能过是为什么。。。
漫步者、 2013-06-20
  • 打赏
  • 举报
回复
不要去引用局部对象的地址,结果导致未定义
ri_aje 2013-06-20
  • 打赏
  • 举报
回复
另个写法都不对,前者不对因为标准要求 & 的运算数为左值,后者不对因为标准要求 A& 只能绑定到 non-const lvalue 对象。不过你可以这样写, A const& a = func(); 你后面说的容器之类的就不报错,因为没见到代码,只能猜你实际用的常引用,所以没问题,就像上面这样。
rocktyt 2013-06-20
  • 打赏
  • 举报
回复
func返回值是右值 不能对右值进行取地址操作,不能对右值进行引用 vector中存放的是值,容器中取元素返回的都是引用,而不是值
ri_aje 2013-06-20
  • 打赏
  • 举报
回复
引用 33 楼 L812234929 的回复:
[quote=引用 27 楼 hsz871204 的回复:] [quote=引用 24 楼 mingliang1212 的回复:] [quote=引用 21 楼 yudahai109 的回复:] 如果能对 右值对象或者临时对象 取地址并且操作,都是右值引用的概念,这个是c++ 11才提出来的。这只是一个概念,不是说,一定要用引用来取,用指针也一样的。 你在c++ 98或者c++ 03上,能对 右值对象 或者 临时对象 取地址? 我还看你不爽呢,自以为有点经验,被别人指出了错误,恼羞成怒了,还不如回去好好看看书。 你可以参考http://skyscribe.github.io/blog/2012/08/19/c-plus-plus-11xin-te-xing-rvalue-reference-and-and-move/这篇文章。
这个问题毫无争议. 无论是C ++ 03, 还是 C++ 11 . 楼主的程序是都是错的. 你在12 楼说是对的,那就是你应该好好看书去了. 至于 VS 2010, 它出来的时候 C++11根本就没有出来. 它为什么支持这种错误的写法, 是因为它有神经病. [/quote] 我到不这么认为: A func() { A a; return a; } func()返回的是a的一个复制的临时对像,一般来说,是用来给别的变量赋值的,赋完值它就销毁了。 A *a = &func(); 这样的操作,是对这个临时对象取地址,并将他赋给指针变量a,此时a指向的就是这个临时变量,并且这个临时变量也不会马上销毁了,而是会将它的生命延长到和指针变量a一样。 所以这个的操作是可以成功的,也不会产生什么后遗症。 A &a = func(); 这样的操作是声明了一个引用a来引用这个临时变量,此时这个临时变量的生命周期也延长到了与引用a相同,所以引用也成功。 仅仅是个人看法,有什么不对的请指证[/quote] 请看#23的例子, A *a = &func(); 并没有延长func()返回的临时变量的生命周期, 但是奇怪的是 A &a = func();竟然把func()返回的临时变量的生命周期延长了,搞不懂为啥子 [/quote] 首先强调一下,这两种写法都是不正确的。 其次是貌似 vs2010 (and vs2012 with warnings) 非得要画蛇添足的支持一下。 然后是 A const& a = func(); 是合法的并且标准保证临时对象的生命期延长至该常量引用的生命期。 最后是我猜 vs2010 就直接把对 const& 的延长生命期特性直接借给 non-const& 用了,反正 const 属性变成汇编以后就不存在了;还有一种可能就是临时对象占用的内存没有被系统覆盖,造成其仍然存在的假象。 总之这些都是 undefined behaviors,没必要纠结,记着别用就好了。
狼异族 2013-06-20
  • 打赏
  • 举报
回复
引用 27 楼 hsz871204 的回复:
[quote=引用 24 楼 mingliang1212 的回复:] [quote=引用 21 楼 yudahai109 的回复:] 如果能对 右值对象或者临时对象 取地址并且操作,都是右值引用的概念,这个是c++ 11才提出来的。这只是一个概念,不是说,一定要用引用来取,用指针也一样的。 你在c++ 98或者c++ 03上,能对 右值对象 或者 临时对象 取地址? 我还看你不爽呢,自以为有点经验,被别人指出了错误,恼羞成怒了,还不如回去好好看看书。 你可以参考http://skyscribe.github.io/blog/2012/08/19/c-plus-plus-11xin-te-xing-rvalue-reference-and-and-move/这篇文章。
这个问题毫无争议. 无论是C ++ 03, 还是 C++ 11 . 楼主的程序是都是错的. 你在12 楼说是对的,那就是你应该好好看书去了. 至于 VS 2010, 它出来的时候 C++11根本就没有出来. 它为什么支持这种错误的写法, 是因为它有神经病. [/quote] 我到不这么认为: A func() { A a; return a; } func()返回的是a的一个复制的临时对像,一般来说,是用来给别的变量赋值的,赋完值它就销毁了。 A *a = &func(); 这样的操作,是对这个临时对象取地址,并将他赋给指针变量a,此时a指向的就是这个临时变量,并且这个临时变量也不会马上销毁了,而是会将它的生命延长到和指针变量a一样。 所以这个的操作是可以成功的,也不会产生什么后遗症。 A &a = func(); 这样的操作是声明了一个引用a来引用这个临时变量,此时这个临时变量的生命周期也延长到了与引用a相同,所以引用也成功。 仅仅是个人看法,有什么不对的请指证[/quote] 请看#23的例子, A *a = &func(); 并没有延长func()返回的临时变量的生命周期, 但是奇怪的是 A &a = func();竟然把func()返回的临时变量的生命周期延长了,搞不懂为啥子
rocktyt 2013-06-20
  • 打赏
  • 举报
回复
引用 25 楼 yudahai109 的回复:
[quote=引用 22 楼 ri_aje 的回复:] [quote=引用 21 楼 yudahai109 的回复:] [quote=引用 19 楼 ri_aje 的回复:] [quote=引用 16 楼 yudahai109 的回复:] [quote=引用 13 楼 ri_aje 的回复:] [quote=引用 12 楼 yudahai109 的回复:] [quote=引用 10 楼 L812234929 的回复:]
class A
{
public:
	A();

	A(const A &a);

public:

	int m_iA;
};

A::A()
{
	m_iA = 0;
}

A::A(const A &a)
{
	m_iA = a.m_iA;
}

A Func1()
{
	A b;
	return b;
}

A &Func2()
{
	A &a = Func1();

	return a;
}

void main()
{
	A *a = &Func2(); 
	A &b = Func1();
	
	a->m_iA = 1;
	printf("a = %d\n", a->m_iA);

	b.m_iA = 2;
	printf("a = %d\n", b.m_iA);
}
执行结果: a = 1 a = 2 Press any key to continue VC++ 6.0 真是奇葩 这都能行
者确定这是用vc++ 6.0?而不是vs2010? 楼主的问题明显是 c++ 11的新特征,非常量的右值引用,c++ 11是支持的,vs2010 和vs2012是支持c++ 11的,所以编译出来正常。 楼主的codeblock升级到最新版本,也应该是支持的。[/quote] 不懂就别忽悠,unary & operator 和 A& 都要求其 operand 为左值,c++11 也是这么要求的。 vs 自家的 2012 都不想支持这种用法,编译能凑活通过,但是有 warnings nonstandard extension used : class rvalue used as lvalue nonstandard extension used : 'initializing' : conversion from 'A' to 'A &'[/quote]

#include <iostream>

using namespace std;

class A
{
public:
   A()
   {
      cout<<"A is created"<<endl;
   }

   void show()
   {
	   cout<<"show something"<<endl;
   }
};
 
A func()
{
    A a;
    return a;
}
 
int main()
{
	A b;
    func() = b;
	func().show();
    return 0;
}
请告诉我,如果不是c++ 11的非常量右值引用,这个程序怎么通过的? [/quote] (1) 这个例子能通过和主楼程序的错误没有任何关系。 (2) 这个例子的正确性不需要 c++11 支持,c++03 的编译器也能够正确编译该程序。 主楼程序错误的原因在于 A *a = &func(); // s1 A &a = func(); // s2 & 只能作用于左值上,A& 只能绑定于非常量左值上。 你给的例子中没有涉及这两个概念中的任何一个,典型的混淆视听。 你在 #12 的主要观点是认为主楼程序是正确的,是受 c++11 支持的。你抱着个"非常两右值引用"的概念忽悠,这也是我看着不爽的原因,实际上主楼程序就没有右值引用,只有右值对象,也叫临时对象。[/quote] 如果能对 右值对象或者临时对象 取地址并且操作,都是右值引用的概念,这个是c++ 11才提出来的。这只是一个概念,不是说,一定要用引用来取,用指针也一样的。 你在c++ 98或者c++ 03上,能对 右值对象 或者 临时对象 取地址? 我还看你不爽呢,自以为有点经验,被别人指出了错误,恼羞成怒了,还不如回去好好看看书。 你可以参考http://skyscribe.github.io/blog/2012/08/19/c-plus-plus-11xin-te-xing-rvalue-reference-and-and-move/这篇文章。[/quote] 呵呵,你搞笑了。你看我不爽没关系,任何人都可以看我不爽。 问题是你号称指出了我的错误,搞来搞去也没说明白我到底那里错了,到是你陈述里被我挑出错误了,要么就是故左右而言它竟说些不相关的。 我从来没说过能对右值对象取地址,这种错误的观点是你强行派给我的,实际上我始终在说取地址只能对左值。目测你为了证明我错了,看来是主动忽略了。 我也用不着恼羞成怒,如果我真的错了,我会很痛快的承认的,任何有兴趣的都可以翻翻我在 csdn 的回帖记录,看看我承认错误的记录。倒是你让我觉得不愿意承认自己错了。[/quote] 我说了,那个只是我的一个失误,打字太快,没有回头看,上面我都sorry了,难不成你没看到? 我的意思就明摆着,c++ 11支持了对 右值引用,这个也是楼主出现的疑问,他说vs2010能通过,codeblocks不能通过。我的回答是,vs2010要么就是利用的c++ 11的特性,要么私自支持了这个特性。codeblocks升级到最新版本也应该可以支持,只要支持c++ 11的都可以。 但您的问题呢,死命说 这不是 c++ 11的右值引用,右值引用的操作数必须是左值。 看你这句话,& 只能作用于左值上,A& 只能绑定于非常量左值上。 而且我一再说“右值引用”只是概念,这要对 右值 能取到地址,都是这个概念的范畴。 再看看我在16#的例子,还需要说明么?真搞笑,您年纪多大了,技术不咋地,脾气跟个老学究一样。看不懂新技术,就不要出来丢人,可以去google搜索一下。就你这样的,我见多了,仗着自己有几年经验,死抱着久规则不放,天天跟怨妇一样,抱怨外面的技术怎么更新这么快。 [/quote] 右值引用是&& 你对一个右值去用&来引用是不行的 你真的知道什么是右值引用吗?
亢奋熊 2013-06-20
  • 打赏
  • 举报
回复
ri_aje 2013-06-20
  • 打赏
  • 举报
回复
引用 25 楼 yudahai109 的回复:
我说了,那个只是我的一个失误,打字太快,没有回头看,上面我都sorry了,难不成你没看到?
看到了,不过你那句话说的上下文不清晰,我根本不知道你在 sorry 什么。
引用 25 楼 yudahai109 的回复:
我的意思就明摆着,c++ 11支持了对 右值引用,这个也是楼主出现的疑问,他说vs2010能通过,codeblocks不能通过。我的回答是,vs2010要么就是利用的c++ 11的特性,要么私自支持了这个特性。codeblocks升级到最新版本也应该可以支持,只要支持c++ 11的都可以。 但您的问题呢,死命说 这不是 c++ 11的右值引用,右值引用的操作数必须是左值。 看你这句话,& 只能作用于左值上,A& 只能绑定于非常量左值上。 而且我一再说“右值引用”只是概念,这要对 右值 能取到地址,都是这个概念的范畴。
这恰恰是错误所在的点,c++11 不支持对右值取地址。vs2010 的支持是画蛇添足并且危险的。你总是歪曲我的陈述,我一直在说 & 的操作数必须是左值。下次你要是再说我死命说什么,麻烦引用一下我到底在那里说的,别总是强行派给我一些莫名其妙的东西。
引用 25 楼 yudahai109 的回复:
再看看我在16#的例子,还需要说明么?真搞笑,您年纪多大了,技术不咋地,脾气跟个老学究一样。看不懂新技术,就不要出来丢人,可以去google搜索一下。就你这样的,我见多了,仗着自己有几年经验,死抱着久规则不放,天天跟怨妇一样,抱怨外面的技术怎么更新这么快。
什么都不需要说明,因为 #16 的例子什么都说明不了。 你嘲笑我也改变不了事实,倒是只会使你看起来恼羞成怒了。 你以为你知道个非常量右值引用就赶上时代潮流了?c++11 标准文档我今年年初的时候就已经全部阅读完了,并且写了 20000+ 行的笔记。什么时候你到这个水平,再来 yy 我需要"抱怨外面的技术怎么更新这么快"吧。 不客气的说你的描述倒是比较符合你的行为。
  • 打赏
  • 举报
回复
加载更多回复(21)

64,640

社区成员

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

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