const的范围引用临时子对象 [英] Scope of const references to subojects of temporaries

查看:62
本文介绍了const的范围引用临时子对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下示例中,第3节在VC98,VC2003,VC2005 Express

Beta(2004年8月)和g ++ 3.3.2下失败。这只是C ++

规范的陷阱吗?为什么上述编译器中没有任何一个至少将此标记为

警告,因为他们会在尝试返回const&到当地?

在第2节中,const B& Bref初始化并绑定到GetSettings()返回的临时

。这是临时B存在,直到Bref超出范围。


第3节中出现的情况是:


1.临时B对象是复制构造并从

GetSettings()调用返回。

2. GetData()调用临时返回一个const&并且没有为其返回创建临时的

,因此Cref2被初始化为参考成员

的临时对象。

3.临时B对象超出范围。

4.然后对已经超出

范围并且不再存在的对象进行Cref2.Test()调用。


当const C& Cref2被初始化为引用

临时B的子对象,这不应该导致临时范围被绑定到Cref2的那个吗?



当我们通过const&返回更改了一个相当大的类

层次结构的A :: GetSettings()时发现了这种行为。而是通过

值返回并且事情退出正常工作。我们认为这是一个线路代码重新考虑的因素,结果却产生了这种令人讨厌的后果。所以我想问,编译器是否在这里正确实现了C ++规范(或者是我们测试过的所有

)?并且,编译器是否会产生错误或警告

警告程序员?


谢谢


- -----在这里剪断-------


#include< cassert>

#include< iostream>

#include< vector>


使用命名空间std;


struct C

{

C():mBuffer(100,0xDC){cerr<< " C()\ n";}

C(const C& c):mBuffer(c.mBuffer){cerr<< " C(const C&)\ n";}

virtual~C(){mBuffer.clear(); cerr<< " ~C()\ n";}


void Test()const {assert(!mBuffer.empty());}


受保护:

vector< char> mBuffer;

};

struct B

{

B(){cerr<< " B()\ n";}

B(const B& b):mData(b.mData){cerr<< B(const B&)\ n;}}

virtual~B(){cerr<< " ~B()\ n";}

const C& GetData()const {return mData;}


protected :

C mData;

};

struct A

{

A (){cerr<< " A()\ n";}

A(const A& a){cerr<< A(const A&)\ n;;}

virtual~A(){cerr<< " ~A()\ n";}


虚拟B GetSettings()const {return mSettings;}


protected:

B mSettings;

};

int main(无效)

{

一个anObject;


// 1。这工作

anObject.GetSettings()。GetData()。Test();


// 2。这也适用

const B& Bref = anObject.GetSettings();

const C& Cref = Bref.GetData();

Cref.Test();


/ *

// 3。这不起作用......没有编译警告或错误,但是断言pops

const C& Cref2 = anObject.GetSettings()。GetData();

Cref2.Test();

* /


cerr<< 结束范围主要()\ n;;

返回0;

}

解决方案

ATASLO写道:

在下面的例子中,第3节在VC98,VC2003,VC2005 Express
Beta(2004年8月)和g ++ 3.3.2下失败。这只是C ++
规范的一个陷阱吗?


是的,只有临时约束才能获得终身延期

参考。


为什么上述编译器中没有任何一个至少将此标记为警告,因为他们在试图返回const&去当地?


我认为编译器更难以检测,至少在一般的

情况下。

In Section #2,const B& Bref初始化并绑定到GetSettings()返回的临时值。这是临时B存在,直到Bref超出范围。

第3节中似乎发生的是:

1.临时B对象是复制构造并从GetSettings()调用返回。
2.对该临时文件的GetData()调用返回一个const&并且没有为其返回创建临时
,因此Cref2被初始化为引用临时对象的成员。
3.临时B对象超出范围。
4.然后对已经超出范围并且不再存在的对象进行Cref2.Test()调用。

当const C& Cref2被初始化为引用临时B的子对象,不应该导致临时范围被绑定到Cref2的范围吗?


对,这就是'正在发生的事情。

当我们改变了一个相当大的类层次结构时发现了这种行为''通过const&返回的A :: GetSettings()改为使用
值返回,并且事情退出正常工作。我们认为是一对
线路代码重新考虑因素,结果却产生了这种令人讨厌的后果。所以我问,编译器是否在这里正确实现了C ++规范(或者我们测试的所有部分都已破坏)?并且,编译器是否会产生错误或警告以提醒程序员?




是,否。我不知道在这种情况下会发出警告的任何编译器,

因为它不是在编译时很容易被检测到的情况。


Tom


" Tom Widmer"写道:

ATASLO写道:

在下面的例子中,第3节失败了VC98,VC2003,VC2005 Express
Beta(2004年8月)和g ++ 3.3.2。这只是C ++
规范的一个缺陷吗?



是的,当临时绑定到引用时,你只能获得终身扩展。




来自C ++标准的第12.2节:

"引用绑定的临时值或
$的临时值b $ b对象的完整对象,临时绑定的子对象持续存在于参考的生命周期或者直到创建

临时范围的范围结束,以先到者为准。


这并不意味着如果引用被绑定的对象

是临时的子对象本身,然后整个临时完成

对象(即父级)的范围也扩展到参考范围。


ATASLO写道:

" Tom Widmer"写道:

ATASLO写道:

>在以下示例中,第3节在VC98,VC2003,VC2005 Express
>下失败。 Beta(2004年8月)和g ++ 3.3.2。这只是C ++的一个陷阱
>规范?



是的,当临时直接绑定到参考时,你才能获得终生延期。



来自第12.2节C ++标准:
"引用绑定的临时对象或者临时绑定的子对象的临时对象是
生命周期的持续时间引用或直到创建
临时的范围结束,以先到者为准。

这并不意味着如果引用被绑定的对象
是临时本身的子对象,然后整个临时完整对象(即父对象)的范围也扩展到引用范围。



这并不仅仅意味着它;它直接说明了它。但是,它并不适用于你的情况,因为你没有约束临时。你有:


/ *

// 3。这不起作用......没有编译警告或错误,但是断言pops

const C& Cref2 = anObject.GetSettings()。GetData();

Cref2.Test();

* /


现在,B :: GetData返回const C&,这是一个引用,所以你是绑定a

参考。至于你的其他情况:


// 1。这工作

anObject.GetSettings()。GetData()。Test();


// 2。这也适用

const B& Bref = anObject.GetSettings();

const C& Cref = Bref.GetData();

Cref.Test();


案例(1)有效,因为它只是一个大表达式,所以任何临时工具

生产直到完整表达的结束。


Case(2)有效,因为A :: GetSettings返回一个B,你绑定到

Bref,所以终身规则适用。然后你可以调用B :: GetData并使用

C&只要Bref仍然在范围内,就会返回,因为它保持B

(包含Cref绑定的C)活着。


-

Doug Harrison

Microsoft MVP - Visual C ++


In the following example, section #3 fails under VC98, VC2003, VC2005 Express
Beta (Aug 2004) and g++ 3.3.2. Is this just a pitfall of the C++
specification? Why don''t any of the above compilers at least flag this as a
warning as they would when say trying to return a const & to a local?

In Section #2, the const B& Bref is initialized and bound to the temporary
returned from GetSettings(). That is the temporary B exists until Bref goes
out of scope.

What appears to be happening in section #3 is this:

1. A temporary B object is copy constructed and returned from the
GetSettings() call.
2. The GetData() call on that temporary returns a const & and no temporary
is created for its return, thus Cref2 is initialized to a reference a member
of a temporary object.
3. The temporary B object goes out of scope.
4. The Cref2.Test() call is then made on an object that has passed out of
scope and no longer exists.

When the const C& Cref2 is initialized to refer to a subobject of the
temporary B shouldn''t that also cause the temporaries scope to be bound to
that of Cref2?

This behavior was discovered when we changed a rather large class
hierarchy''s A::GetSettings() from returning by const & to be a return by
value instead and things quit working correctly. What we thought was a
couple line code re-factor turned out to have this nasty consequence. So I
ask, is the compiler correctly implementing the C++ spec here (or are all the
ones we tested broken)? And, can the compiler produce an error or warning to
alert the programmer?

Thanks

-------snip below here-------

#include <cassert>
#include <iostream>
#include <vector>

using namespace std;

struct C
{
C() : mBuffer(100, 0xDC) {cerr << "C()\n";}
C(const C &c) : mBuffer(c.mBuffer) {cerr << "C(const C &)\n";}
virtual ~C() {mBuffer.clear(); cerr << "~C()\n";}

void Test() const {assert(!mBuffer.empty());}

protected:
vector<char> mBuffer;
};
struct B
{
B() {cerr << "B()\n";}
B(const B &b) : mData(b.mData) {cerr << "B(const B &)\n";}
virtual ~B() {cerr << "~B()\n";}

const C &GetData() const {return mData;}

protected:
C mData;
};
struct A
{
A() {cerr << "A()\n";}
A(const A &a) {cerr << "A(const A &)\n";}
virtual ~A() {cerr << "~A()\n";}

virtual B GetSettings() const {return mSettings;}

protected:
B mSettings;
};
int main(void)
{
A anObject;

//1. This works
anObject.GetSettings().GetData().Test();

//2. This works as well
const B &Bref = anObject.GetSettings();
const C &Cref = Bref.GetData();
Cref.Test();

/*
//3. This doesn''t work...no compile warnings or errors, but assert pops
const C &Cref2 = anObject.GetSettings().GetData();
Cref2.Test();
*/

cerr << "End Scope of main()\n";
return 0;
}

解决方案

ATASLO wrote:

In the following example, section #3 fails under VC98, VC2003, VC2005 Express
Beta (Aug 2004) and g++ 3.3.2. Is this just a pitfall of the C++
specification?
Yes, you only get lifetime extension when a temporary is directly bound
to a reference.

Why don''t any of the above compilers at least flag this as a warning as they would when say trying to return a const & to a local?
I think it''s harder for the compiler to detect, at least in the general
case.
In Section #2, the const B& Bref is initialized and bound to the temporary
returned from GetSettings(). That is the temporary B exists until Bref goes
out of scope.

What appears to be happening in section #3 is this:

1. A temporary B object is copy constructed and returned from the
GetSettings() call.
2. The GetData() call on that temporary returns a const & and no temporary
is created for its return, thus Cref2 is initialized to a reference a member
of a temporary object.
3. The temporary B object goes out of scope.
4. The Cref2.Test() call is then made on an object that has passed out of
scope and no longer exists.

When the const C& Cref2 is initialized to refer to a subobject of the
temporary B shouldn''t that also cause the temporaries scope to be bound to
that of Cref2?
Right, that''s what''s happening.

This behavior was discovered when we changed a rather large class
hierarchy''s A::GetSettings() from returning by const & to be a return by
value instead and things quit working correctly. What we thought was a
couple line code re-factor turned out to have this nasty consequence. So I
ask, is the compiler correctly implementing the C++ spec here (or are all the
ones we tested broken)? And, can the compiler produce an error or warning to
alert the programmer?



Yes, and no. I don''t know of any compiler that warns in this situation,
since it isn''t a situation that can easily be detected at compile time.

Tom


"Tom Widmer" wrote:

ATASLO wrote:

In the following example, section #3 fails under VC98, VC2003, VC2005 Express
Beta (Aug 2004) and g++ 3.3.2. Is this just a pitfall of the C++
specification?



Yes, you only get lifetime extension when a temporary is directly bound
to a reference.



From section 12.2 of the C++ standard:
"The temporary to which the reference is bound or the temporary that is the
complete object to a subobject of which the temporary is bound persists for
the lifetime of the reference or until the end of the scope in which the
temporary is created, whichever comes first."

Doesn''t that imply that if the object to which the reference is being bound
is a subobject of a temporary itself, then the entire temporary complete
object''s (ie parent''s) scope is extended to that of the reference as well.


ATASLO wrote:

"Tom Widmer" wrote:

ATASLO wrote:

> In the following example, section #3 fails under VC98, VC2003, VC2005 Express
> Beta (Aug 2004) and g++ 3.3.2. Is this just a pitfall of the C++
> specification?



Yes, you only get lifetime extension when a temporary is directly bound
to a reference.



From section 12.2 of the C++ standard:
"The temporary to which the reference is bound or the temporary that is the
complete object to a subobject of which the temporary is bound persists for
the lifetime of the reference or until the end of the scope in which the
temporary is created, whichever comes first."

Doesn''t that imply that if the object to which the reference is being bound
is a subobject of a temporary itself, then the entire temporary complete
object''s (ie parent''s) scope is extended to that of the reference as well.



It doesn''t just imply it; it directly states it. However, it doesn''t apply
in your case, because you''re not binding a temporary. You had:

/*
//3. This doesn''t work...no compile warnings or errors, but assert pops
const C &Cref2 = anObject.GetSettings().GetData();
Cref2.Test();
*/

Now, B::GetData returns const C&, which is a reference, so you''re binding a
reference. As for your other cases:

//1. This works
anObject.GetSettings().GetData().Test();

//2. This works as well
const B &Bref = anObject.GetSettings();
const C &Cref = Bref.GetData();
Cref.Test();

Case (1) works because it''s all one big expression, so any temporaries
produced live until the end of the full-expression.

Case (2) works because A::GetSettings returns a B, which you''re binding to
Bref, so the lifetime rule applies. Then you can call B::GetData and use the
C& returned as long as Bref is still in scope, because it''s keeping the B
(which contains the C to which Cref is bound) alive.

--
Doug Harrison
Microsoft MVP - Visual C++


这篇关于const的范围引用临时子对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆