2

Have a look at the following code:

struct Foo;

Foo* bar;

struct Foo {
    void func() const {
        bar = this;
    }
}

int main() {
    Foo().func();
}        

This does not work as bar won't accept a const Foo*. To get around this, const_cast could be used:

struct Foo {
    void func() const {
        bar = const_cast<Foo*>(this);
    }
}

Is this safe? I'm very cautious when it comes to using const_cast, but in this case it seems legit to me.

csk
  • 313
  • 2
  • 11
  • Why do you think it is not safe? Also, why not just do `main() { bar = new Foo(); }` – CinCout Aug 23 '17 at 10:54
  • Doesn't seem legit to me. The `this` pointer in `func()` is `const` saying "Here is a pointer to the object through which it should not be modifed". You've then assigned it to a pointer through which is may be modifed. Remove `const` from `func()` or add it to `bar` (`const Foo *bar;`). – Persixty Aug 23 '17 at 10:55
  • 2
    Whatever the underlying reason for this, this is wrong. What is the real problem are you trying to solve. No, not the one that involves `const_cast`, but the problem to whose solution you think involves using `const_cast`. – Sam Varshavchik Aug 23 '17 at 10:55

3 Answers3

4

No, this is potentially dangerous.

func() is marked const which means that it can be called by a const object:

const Foo foo;
foo.func();

Because this is const Foo*.

If you const_cast away the const you end up with a Foo* to a const object. This means that any modification to that object through the non-const pointer (or through any copy of that pointer, bar in this case) will get you undefined behavior since you are not allowed to modify a const object (duh).

Plus the obvious problem is that you're lying to the consumer of class Foo by saying func() won't modify anything while you're doing the opposite.

const_cast is almost never correct and this seems like an XY-problem to me.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • 1
    "`const_cast` is almost never correct " : The obvious exception is something like `std::strch` which is overloaded to return a const pointer given a const argument, and non-const given a non-const argument. One of the overloads will call the other with a const cast. – Martin Bonner supports Monica Aug 23 '17 at 12:39
  • The other exception is where you have a crappy legacy API which has just forgotten to use `const`. (But beware crappy legacy API's which actually modify the argument and then restore it - I'm looking at you [`CreateProcess`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx)) – Martin Bonner supports Monica Aug 23 '17 at 12:41
2

If by "safe" you mean "not undefined behavior" then yes, it is safe. The value of bar will point to the created object as you'd expect.

However, const_cast is generally not recommended because it breaks the convention that a const thing will not be changed, and can easily produce undefined behavior (see this comment below). In this case you can simply do:

struct Foo {
    void func() const {
        bar = const_cast<Foo*>(this);
        bar->modify();
    }
    void modify() { ... }
}

And you will be modifying the object in a const method, which is unexpected in general and undefined behavior if the instance of Foo on which the method is called was initially declared as const. However, it is up to you to get the logic right. Just note that everyone else will expect const things to be const, including the standard library, and usually (if not always) a better code design is possible.

jdehesa
  • 58,456
  • 7
  • 77
  • 121
  • It's not just a convention that const things will not be changed. That call to `modify` is undefined behaviour. (But you are correct that the assignment to bar is fine.) – Martin Bonner supports Monica Aug 23 '17 at 12:43
  • @MartinBonner Really? I thought that was what `const_cast` is for (I don't think I've ever actually used in a real program, really), and only "highly dangerous", specially [since C++11](https://stackoverflow.com/questions/14127379/does-const-mean-thread-safe-in-c11), but not necessarily UB. Could you please point me to some explanation about that? – jdehesa Aug 23 '17 at 15:42
  • 2
    More precisely, it is UB if `func` is called on an object which was originally declared `const`. So `Foo f; f.func();` is fine, but `const Foo f; f.func();` is UB. See 7.1.6.1 para 4 in [n4296](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf) "Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior." – Martin Bonner supports Monica Aug 23 '17 at 16:01
0

Under some circumstances it is safe, namely when you have a non-const Foo object. If you have a const Foo object however, you're not allowed to modify it, and the compiler will not catch the bug because of the cast. So it is not a good idea to use this.

Note that in your example Foo is a temporary which gets destroyed on the next line of main().

alain
  • 11,939
  • 2
  • 31
  • 51