const 成员和赋值运算符.如何避免未定义的行为? [英] const member and assignment operator. How to avoid the undefined behavior?

查看:24
本文介绍了const 成员和赋值运算符.如何避免未定义的行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

回答了关于std::vector of objects and const-correctness,并收到关于未定义行为的评论.我不同意,因此我有一个问题.

I answered the question about std::vector of objects and const-correctness, and received a comment about undefined behavior. I do not agree and therefore I have a question.

考虑具有 const 成员的类:

Consider the class with const member:

class A { 
public: 
    const int c; // must not be modified! 
    A(int c) : c(c) {} 
    A(const A& copy) : c(copy.c) { }     
    // No assignment operator
}; 

我想要一个赋值运算符,但我不想使用 const_cast 就像在以下答案之一的代码中一样:

I want to have an assignment operator but I do not want to use const_cast like in the following code from one of the answers:

A& operator=(const A& assign) 
{ 
    *const_cast<int*> (&c)= assign.c;  // very very bad, IMHO, it is undefined behavior
    return *this; 
} 

我的解决方案是

// Custom-defined assignment operator
A& operator=(const A& right)  
{  
    if (this == &right) return *this;  

    // manually call the destructor of the old left-side object
    // (`this`) in the assignment operation to clean it up
    this->~A(); 
    // use "placement new" syntax to copy-construct a new `A` 
    // object from `right` into left (at address `this`)
    new (this) A(right); 
    return *this;  
}  

我是否有未定义的行为 (UB)?

没有 UB 的解决方案是什么?

What would be a solution without UB?

推荐答案

您的代码导致未定义的行为.

Your code causes undefined behavior.

不仅仅是未定义,如果 A 用作基类,并且这个、那个或另一个".实际上未定义,总是.return *this 已经是 UB,因为 this 不能保证引用新对象.

Not just "undefined if A is used as a base class and this, that or the other". Actually undefined, always. return *this is already UB, because this is not guaranteed to refer to the new object.

具体来说,请考虑 3.8/7:

Specifically, consider 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:

...

——原始对象的类型为不是 const 限定的,并且,如果一个类类型,不包含任何非静态类型为的数据成员const 限定或引用类型,

— 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,

现在,在对象的生命周期结束后,在该对象占用的存储空间被重用或释放之前,在原始对象占用的存储位置创建一个新对象"正是您正在做的.

Now, "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" is exactly what you are doing.

您的对象属于类类型,并且它确实包含类型为常量限定的非静态数据成员.因此,在您的赋值运算符运行后,引用旧对象的指针、引用和名称​​不能保证引用新对象并可用于操作它.

Your object is of class type, and it does contain a non-static data member whose type is const-qualified. Therefore, after your assignment operator has run, pointers, references and names referring to the old object are not guaranteed to refer to the new object and to be usable to manipulate it.

作为可能出错的具体示例,请考虑:

As a concrete example of what might go wrong, consider:

A x(1);
B y(2);
std::cout << x.c << "
";
x = y;
std::cout << x.c << "
";

期待这个输出吗?

1
2

错了!您可能会得到该输出是合理的,但 const 成员是 3.8/7 中规定的规则的例外的原因是,编译器可以将 x.c 视为它声称的 const 对象.换句话说,允许编译器将这段代码视为:

Wrong! It's plausible you might get that output, but the reason const members are an exception to the rule stated in 3.8/7, is so that the compiler can treat x.c as the const object that it claims to be. In other words, the compiler is allowed to treat this code as if it was:

A x(1);
B y(2);
int tmp = x.c
std::cout << tmp << "
";
x = y;
std::cout << tmp << "
";

因为(非正式地)const 对象不会改变它们的值.在优化涉及 const 对象的代码时,这种保证的潜在价值应该是显而易见的.为了有任何方法可以在不调用 UB 的情况下修改 x.c ,这个保证必须被删除.所以,只要标准作家没有错误地完成他们的工作,就没有办法做你想做的事.

Because (informally) const objects do not change their values. The potential value of this guarantee when optimizing code involving const objects should be obvious. For there to be any way to modify x.c without invoking UB, this guarantee would have to be removed. So, as long as the standard writers have done their job without errors, there is no way to do what you want.

[*] 事实上,我对使用 this 作为放置 new 的参数有疑问 - 可能你应该先将它复制到 void*,然后使用那.但我并不担心这是否是 UB,因为它不会保存整个功能.

[*] In fact I have my doubts about using this as the argument to placement new - possibly you should have copied it to a void* first, and used that. But I'm not bothered whether that specifically is UB, since it wouldn't save the function as a whole.

这篇关于const 成员和赋值运算符.如何避免未定义的行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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