根据我对 href="https://stackoverflow.com/questions/7580635/how-to-copy-or-swap-objects-of-a-type-that-contains-members-that-are-references/">这个问题< /a>,显然:
以下代码是允许的
struct Foo {
int x;
};
Foo f;
Foo & f_ref = f;
(&f) -> ~Foo ();
new (&f) Foo ();
int x = f_ref .x;
,但以下代码不允许允许的,
struct Foo {
const int & x; // difference is const reference
Foo (int & i) : x(i) {}
};
int i;
Foo f (i);
Foo & f_ref = f;
(&f) -> ~Foo ();
new (&f) Foo (i);
int x = f_ref .x;
因为$3.8/7
如果在一个对象的生命周期结束后,在该对象所占用的存储空间被重用或释放之前,在原对象所占用的存储位置上创建了一个新的对象,一个指向原对象的指针,引用原始对象的引用或原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,就可以用来操作新对象,如果:
- 原始对象的类型不是 const 限定的,并且,如果是类类型,不包含任何类型为 const 限定的非静态数据成员或引用类型 .. .
我可以理解当 f 停止时对 fx
的引用如何失效存在,但我不明白为什么 f_ref
应该纯粹因为它的成员之一是 const 和/或引用而无效:它是对 Foo
的引用> 在 and 之前是对 a 的引用之后是Foo
。
有人可以解释一下这种情况背后的理由吗?
编辑
感谢您的回答。我不相信“保证它不会改变”的说法,因为我们不允许目前允许优化器缓存引用,例如:
struct Foo {
const int & x;
Foo (const int & i) : x(i) {}
void do_it ();
};
int i;
Foo f (i);
const int & ii = f.x;
f .do_it (); // may modify i
std :: cout << ii; // May NOT use cached i
我不明白do_it
允许使引用值无效,但 operator new
不允许 -- 序列点使缓存值无效:为什么删除/放置新应该被豁免?
Following the discussion on my answer to this question, apparently:
the following code is allowed
struct Foo {
int x;
};
Foo f;
Foo & f_ref = f;
(&f) -> ~Foo ();
new (&f) Foo ();
int x = f_ref .x;
but the following code is not allowed
struct Foo {
const int & x; // difference is const reference
Foo (int & i) : x(i) {}
};
int i;
Foo f (i);
Foo & f_ref = f;
(&f) -> ~Foo ();
new (&f) Foo (i);
int x = f_ref .x;
Because of $3.8/7
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
- the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type ...
I can understand how a reference to f.x
could be invalidated when f ceases to exist, but I don't see why f_ref
should be invalidated purely because one of its members is const and/or reference and not otherwise: it was a reference to a Foo
before and is a reference to a Foo
afterwards.
Can someone please explain the rationale behind this condition?
Edit
Thanks for the answers. I don't buy the "guarantee it doesn't change" argument because we don't currently allow optimisers to cache referands, for example:
struct Foo {
const int & x;
Foo (const int & i) : x(i) {}
void do_it ();
};
int i;
Foo f (i);
const int & ii = f.x;
f .do_it (); // may modify i
std :: cout << ii; // May NOT use cached i
I don't see how do_it
is allowed to invalidate referenced values but operator new
isn't -- Sequence points invalidate cached values: why should delete/placement-new be exempt?
发布评论
评论(4)
我相信这样做的动机是允许编译器缓存 const 对象的值(注意这是 const 对象,而不仅仅是指向 const 的指针和引用的引用) const),以及跨调用未知代码的引用的引用地址。
在第二个示例中,编译器首先可以“看到”该对象已被创建和销毁,其次是使用相同的值重新创建了该对象。但该标准的作者希望允许编译器将此代码转换
为:
因为
f
的引用成员无法重新定位,因此必须仍然引用 <代码>我。析构和构造操作违反了引用成员 x 的不可重用性。这种优化应该不会特别有争议:考虑以下示例,使用
const
对象而不是带有const
或引用成员的对象:这里
i
是一个编译时常量,some_function_in_another_TU
无法有效地销毁它并用不同的值创建另一个int
来代替它。因此,应该允许编译器发出std::cout << 的代码。 1;
这个想法是,对于其他类型的 const 对象和引用来说,同样应该如此。如果对未知代码的调用可以重新定位引用成员,或更改
const
数据成员的值,则该语言的一个有用的不变量(引用永远不会重新定位,并且 const 对象永远不会更改其值)将被打破。I believe the motivation is to permit the compiler to cache the values of
const
objects (note that's const objects, not merely referands of pointers-to-const and reference-to-const), and the addresses of referands of references, across calls to unknown code.In your second example, the compiler can "see" firstly that the object has been created and destroyed, and secondly that it was re-created using the same value. But the authors of the standard wanted compilers to be allowed to turn this code:
Into this:
because the reference member of
f
cannot be reseated, and hence must still refer toi
. The destruct-and-construct operation violates the non-reaseatable-ness of the reference memberx
.This optimization should not be particularly controversial: consider the following example, using a
const
object rather than an object with aconst
or reference member:Here
i
is a compile-time constant,some_function_in_another_TU
cannot validly destroy it and create anotherint
in its place with a different value. So the compiler should be allowed to emit code forstd::cout << 1;
The idea is that the same should be true by analogy for const objects of other types, and for references.If a call to unknown code could reseat a reference member, or alter the value of a
const
data member, then a useful invariant of the language (references are never reseated and const objects never change their values) would be broken.据我所知,这只是语义正确性以及优化器可能做出的一致假设的问题。考虑一下:
对象
z
可能合理地期望其Foo
成员引用是常量,因此引用important
。正如标准所说,最后两行中的破坏加新构造“自动更新所有引用以引用(逻辑上)新对象”,因此现在z
内的 const-reference 已更改,尽管始终如一的承诺。为了避免这种对常量正确性的暗中破坏,整个就地重建是被禁止的。
As far as I can tell, it's just a matter of semantic correctness, and the adherent assumptions that the optimizer may make. Consider this:
The object
z
may reasonably expect itsFoo
member reference to be constant and thus refer toimportant
. As the standard says, the destruction plus new construction in the last two lines "automatically updates all references to refer to the (logically) new object", so now the const-reference insidez
has changed, despite the promise of being constant.To avoid this backstabbing violation of const-correctness, the entire reconstruction-in-place is forbidden.
优化。假设我有:
编译器看到一个 const int 对象后,有权假设
该值不会改变;优化器可能只是保留该值
在寄存器中放置新的位置,然后再次输出。
请注意,您的示例实际上完全不同。数据成员有
类型
int const&
;它是一个引用(引用始终是 const),因此编译器可以假设引用始终引用相同的内容
目的。 (这个对象的值可能会改变,除非对象本身
也是 const。)编译器
然而,不能对它所引用的对象的值做出这样的假设,因为
i
(就你而言)可以明显改变。这是事实,参考
它本身(像所有引用一样)是不可变的,这会导致此处的未定义行为,而不是
const
你写的。
Optimization. Suppose I have:
The compiler, having seen a
const int
object, has the right to supposethat the value doesn't change; the optimizer might simply keep the value
in a register accross the placement new, and output it again.
Note that your example is actually quite different. The data member has
type
int const&
; it is a reference (and references are always const),so the compiler can assume that the reference always refers to the same
object. (The value of this object may change, unless the object itself
is also const.) The compiler
can make no such assumption about the value of the object it refers to, however, since
i
(in your case) can clearly change. It is the fact that the reference
itself (like all references) is immutable that causes the undefined behavior here, not the
const
that you've written.
如果某些东西是 const 限定的,那么你不应该修改它。延长其使用寿命是一种会带来严重后果的修改。 (例如,考虑析构函数是否有副作用。)
If something is const-qualified, you're not supposed to modify it. Extending its lifetime is a modification with serious consequences. (For example, consider if the destructor has side-effects.)