放置新的常量和引用? [英] Placement new breaks consts and references?

查看:145
本文介绍了放置新的常量和引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在讨论我的回答此问题,显然:



以下代码允许

  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 数据成员的值,然后一个有用的语言的不变量(引用从不重复,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屋!

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