When can the value change when removing const from const_cast?

In accordance with ยง7.1. 5.1 / 4:

Except that any member of a class declared mutable (7.1.1) is mutable, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

So my question becomes: when is an object a const object?

Specifically, is a const member in a non-const object treated as a const object?

class Foo {
    const Bar bar;

    void replaceBar(Bar bar2) {
        *(const_cast<Bar *>&bar) = bar2;  // Undefined behavior?
    }
}

      

This is because I have an immutable class (all fields are constants), but I want to have a move constructor that technically modifies the passed value. I'm fine with the "trick" in this case as it doesn't violate the boolean constant.

+3


source to share


2 answers


A simple rule of thumb is: you can drop a constant if the original object is not a constant. So if you have a non-cont object and, say, you pass a const reference to it in a function, it is legal to drop the constant in the function. In your example, the original object is const, so the casting constant is undefined behavior.



+2


source


Let's make it a complete example:

struct Bar { int x; };

struct Foo {
  const Bar bar;
  Foo( int x ):bar(x) {}

  void replaceBar(Bar bar2) {
    *(const_cast<Bar *>&bar) = bar2;  // Undefined behavior?
  }
};

      

now, let's break the world.

int main() {
  Foo f(3);
  Bar b = {2};
  f.replaceBar(b);
  std::cout << f.bar.x << "\n";
}

      

the above can and probably should output 3 because the const

object Bar

was created with x=3

. The compiler can and should assume that the object const

will not change throughout its lifetime.

Let's break the world more:



struct Bar {
  int* x;
  Bar(int * p):x(p) {}
  ~Bar(){ if (x) delete x; }
  Bar(Bar&& o):x(o.x){o.x=nullptr;}
  Bar& operator=(Bar&& o){
    if (x) delete x;
    x = o.x;
    o.x = nullptr;
  }
  Bar(Bar const&)=delete;
  Bar& operator=(Bar const&)=delete;
};

struct Foo {
  const Bar bar;
  Foo( int* x ):bar(x) {}

  void replaceBar(Bar bar2) {
    *(const_cast<Bar *>&bar) = bar2;  // Undefined behavior?
  }
};

      

now the same game can cause the compiler to delete something twice.

int main() {
  int* p1 = new int(3);
  Foo f( p1 );
  Bar b( new int(2) );
  f.replaceBar(std::move(b));
}

      

and the compiler will remove p1

once inside replaceBar

and must remove it also at the end main

. He can do this because you guaranteed to f.bar.x

remain unchanged ( const

) until the end of his scope, then you broke that promise in replaceBar

.

Now, this is just what makes sense to the compiler: the compiler can literally do anything after you've changed the object declared const

, since you've invoked undefined behavior, Nasal daemons, time travel are all to capture.

Compilers take advantage of the fact that some behavior is undefined (aka, not allowed) for optimization.

0


source







All Articles