放置新的常量和引用? [英] Placement new breaks consts and references?
问题描述
在讨论我的回答到此问题,显然:
以下代码是允许
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; //差异是const引用
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的任何非静态数据成员,或者引用类型 ...
fx
可以在f不存在时失效,但我不明白为什么 f_ref
的成员是const和/或引用,而不是其他:它是对 Foo
的引用,并且是对 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(); //可以修改i
std :: cout<< ii; //可能不使用缓存的i
我看不到如何 do_it
允许使引用值无效,但 operator new
不允许 - 序列点使缓存的值无效:为什么应该删除/ placement-new豁免?
我相信动机是允许编译器缓存 const
对象的值(注意是const objects
在你的代码中,指向指向const和引用的引用的地址,而不是指向指向const和引用到const的引用。第二个例子,编译器可以首先看到对象已经被创建和销毁,其次,它是使用相同的值重新创建的。但标准的作者希望编译器允许转换这个代码:
struct Foo {
const int& ; X;
Foo(int& i):x(i){}
};
int i = 1;
Foo f(i);
some_function_in_another_TU(& f);
std :: cout<< f.x;
进入:
struct Foo {
const int& X;
Foo(int& i):x(i){}
};
int i = 1;
Foo f(i);
some_function_in_another_TU(& f);
std :: cout<<一世; // this line is optimized
因为 f
不能重复,因此必须仍然引用 i
。破坏和构造操作违反了引用成员 x
的不可重写性。
应该不是特别有争议的:考虑下面的例子,使用 const
对象,而不是一个具有 const
成员:
const int i = 1;
some_function_in_another_TU(& i);
std :: cout<<一世;
这里 i
是一个编译时常数, some_function_in_another_TU
无法有效地销毁它,并在其位置创建另一个具有不同值的 int
。所以编译器应该允许为 std :: cout
如果调用未知代码可以重置一个引用成员,或者改变一个 const
数据成员的值,然后一个有用的语言的不变量(引用从不重复,const对象从不改变它们的值)将被打破。
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?
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:
struct Foo {
const int & x;
Foo (int & i) : x(i) {}
};
int i = 1;
Foo f(i);
some_function_in_another_TU(&f);
std::cout << f.x;
Into this:
struct Foo {
const int & x;
Foo (int & i) : x(i) {}
};
int i = 1;
Foo f(i);
some_function_in_another_TU(&f);
std::cout << i; // this line is optimized
because the reference member of f
cannot be reseated, and hence must still refer to i
. The destruct-and-construct operation violates the non-reaseatable-ness of the reference member x
.
This optimization should not be particularly controversial: consider the following example, using a const
object rather than an object with a const
or reference member:
const int i = 1;
some_function_in_another_TU(&i);
std::cout << i;
Here i
is a compile-time constant, some_function_in_another_TU
cannot validly destroy it and create another int
in its place with a different value. So the compiler should be allowed to emit code for std::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.
这篇关于放置新的常量和引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!