const成员和赋值运算符。如何避免UB? [英] const member and assigment operator. How to avoid the UB?

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

问题描述

回答了关于std::对象和const正确性的向量,并得到不受欢迎的 downvote和关于UB的评论。我不同意,因此我有一个问题。



考虑使用const成员的类:

  class A {
public:
const int c; //不能修改!
A(int c):c(c){}
A(const A& copy):c(copy.c){}
//无赋值运算符
} ;

我想有一个赋值运算符,但我不想使用 const_cast ,如以下代码中的一个答案:

  operator =(const A& assign)
{
* const_cast< int *> (& c)= assign.c; //非常非常糟糕,IMHO,它是UB
return * this;
}



我的解决方案是

  A& operator =(const A& right)
{
if(this ==& right)return * this;
this->〜A()
new(this)A(right);
return * this;
}

我有未定义的行为吗? >

您的解决方案没有UB。

解决方案

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



不只是未定义,如果A用作基类,这个,那个或其他。实际上未定义,总是。 return * this 已经是UB,因为这个不能保证引用新对象。



具体来说,考虑3.8 / 7:


如果在对象的生命周期后
已经结束并且在存储之前
所占用的对象被重用或
释放,在
处创建新对象
原始对象占用的存储位置,指针$指向原始对象的b $ b,引用
原始对象的
引用或
原始对象的名称将自动
引用新对象, ,一旦新对象的
生存期开始,
可以用于操作
新对象,如果:



。 ..



- 原始对象的类型是
不是const限定的,如果类
类型不包含任何非-static
类型为
const限定或引用类型的数据成员,


对象的生命周期已经结束,并且在重新使用或释放​​对象所占用的存储之前,在原始对象占用的存储位置创建一个新对象正是您在做什么。



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



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

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

期望输出?

  1 
2

错误!这是可能的,你可能得到那个输出,但const成员是3.8 / 7中规定的规则的一个例外的原因是,编译器可以将 xc 作为const对象它声称是。换句话说,编译器被允许处理这个代码,如同它是:

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

因为(非正式) const对象不改变它们的值。当优化包含const对象的代码时,这种保证的潜在价值应该是显而易见的。对于有任何方式修改 x.c 而不调用UB,此保证必须删除。所以,只要标准作家做了他们的工作没有错误,没有办法做你想要的。



[*]事实上,我有我的疑问使用 this 作为placement new的参数 - 可能你应该先将它复制到 void * 。但我不打扰是否具体是UB,因为它不会保存作为一个整体的功能。


I answered the question about std::vector of objects and const-correctness and got undeserved downvote and a comment about UB. I do not agree and therefore I have a question.

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

I want to have an assignment operator but I does 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 UB
    return *this; 
} 

My solution is

A& operator=(const A& right)  
{  
    if (this == &right) return *this;  
    this->~A() 
    new (this) A(right); 
    return *this;  
}  

Do I have undefined behavior?

Please, your solution without UB.

解决方案

Your code causes undefined behavior.

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.

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:

...

— 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 << "\n";
x = y;
std::cout << x.c << "\n";

Expect this output?

1
2

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 << "\n";
x = y;
std::cout << tmp << "\n";

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.

[*] 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成员和赋值运算符。如何避免UB?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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