放置新的断点和引用
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; // 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.x
的引用在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 (); // may modify i
std :: cout << ii; // May NOT use cached i
我不明白do_it
是如何被允许使引用值无效,但operator new
不是——序列点使缓存值无效:为什么要删除/放置新豁免?
我相信这样做的动机是允许编译器缓存const
对象的值(注意,这是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 << i; // this line is optimized
是因为f
的引用成员不能被重坐,因此必须仍然指向i
。析构操作违反了引用成员x
的非合理性。
这个优化不应该特别有争议:考虑下面的例子,使用const
对象而不是具有const
或引用成员的对象:
const int i = 1;
some_function_in_another_TU(&i);
std::cout << i;
这里i
是一个编译时常量,some_function_in_another_TU
不能有效地销毁它,并在它的位置创建另一个int
,并使用不同的值。因此,编译器应该被允许为std::cout << 1;
发出代码。这个想法是,对于其他类型的const对象和引用,同样应该是正确的。
如果对未知代码的调用可以重置引用成员,或者改变const
数据成员的值,那么该语言的一个有用的不变量(引用永远不会重置,const对象永远不会改变它们的值)将被破坏。
据我所知,这只是语义正确性的问题,以及优化器可能做出的相应假设。想想看:
Bar important, relevant;
Foo x(important); // binds as const-reference
Zoo z(x); // also binds as const reference
do_stuff(z);
x.~Foo();
::new (&x) Foo(relevant); // Ouch?
对象z
可以合理地期望其Foo
成员引用是常量,从而引用important
。正如标准所说,最后两行中的销毁加新构造"自动更新所有引用以引用(逻辑上)新对象",因此现在z
内部的const-reference发生了变化,尽管承诺是常量。
为了避免这种对const-正确性的暗箭式违反,禁止整个就地重建。
优化。假设我有:
struct Foo
{
int const x;
Foo( int init ) : x( init ) {}
};
int
main()
{
Foo a( 42 );
std::cout << a.x << std::endl;
new (&a) Foo( 3 );
std::cout << a.x << std::endl;
return 0;
}
编译器看到const int
对象后,有权假设值不会改变;优化器可能只是保留该值在寄存器中跨放置new,并再次输出。
请注意,您的示例实际上是完全不同的。数据成员有类型int const&
;它是一个引用(引用总是const),因此编译器可以假设引用总是指向相同的对象对象。(该对象的值可能会改变,除非该对象本身也是const。)编译器不能对其所指对象的值做出这样的假设,然而,由于i
(在你的情况下)可以明显改变。这是事实,参考本身(像所有引用一样)是不可变的,导致这里的未定义行为,而不是const
如果某些内容是const限定的,则不应该修改它。延长其使用寿命是一种会带来严重后果的修改。(例如,考虑析构函数是否有副作用。)
- 将对象数组的引用传递给函数
- 什么时候在C++中返回常量引用是个好主意
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 何时在引用或唯一指针上使用移动语义
- 如何在c++中使用引用实现类似python的行为
- 编译C++时未定义的引用
- Ctypes wstring通过引用传递
- c++r值引用应用于函数指针
- 理解c++中的引用
- C++取消引用指针.为什么会发生变化
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 强制转换为引用类型
- 引用一个已擦除类型(void*)的指针
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 具有默认值的引用获取函数
- 如何在引用上设置数据断点
- 放置新的断点和引用
- 在 Visual Studio 2005 中对取消引用的指针的地址设置数据断点