放置新的中断常量和引用?

发布于 2024-12-07 01:37:35 字数 1951 浏览 1 评论 0 原文

根据我对 href="https://stackoverflow.com/questions/7580635/how-to-copy-or-swap-objects-of-a-type-that-c​​ontains-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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

扮仙女 2024-12-14 01:37:35

我相信这样做的动机是允许编译器缓存 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 的引用成员无法重新定位,因此必须仍然引用 <代码>我。析构和构造操作违反了引用成员 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 对象永远不会更改其值)将被打破。

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.

少女七分熟 2024-12-14 01:37:35

据我所知,这只是语义正确性以及优化器可能做出的一致假设的问题。考虑一下:

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 已更改,尽管始终如一的承诺。

为了避免这种对常量正确性的暗中破坏,整个就地重建是被禁止的。

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:

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?

The object z may reasonably expect its Foo member reference to be constant and thus refer to important. 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 inside z has changed, despite the promise of being constant.

To avoid this backstabbing violation of const-correctness, the entire reconstruction-in-place is forbidden.

日记撕了你也走了 2024-12-14 01:37:35

优化。假设我有:

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 对象后,有权假设
该值不会改变;优化器可能只是保留该值
在寄存器中放置新的位置,然后再次输出。

请注意,您的示例实际上完全不同。数据成员有
类型int const&;它是一个引用(引用始终是 const),
因此编译器可以假设引用始终引用相同的内容
目的。 (这个对象的值可能会改变,除非对象本身
也是 const。)编译器
然而,不能对它所引用的对象的值做出这样的假设,因为 i
(就你而言)可以明显改变。这是事实,参考
它本身(像所有引用一样)是不可变的,这会导致此处的未定义行为,而不是 const
你写的。

Optimization. Suppose I have:

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;
}

The compiler, having seen a const int object, has the right to suppose
that 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.

小兔几 2024-12-14 01:37:35

如果某些东西是 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.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文