6

I have a back up copy of data that I would like to protect so I made it const. I need to violate that constness on two occassions, once to store virgin data to it:

fgBlocks.CopyInto((BlkArray&)backUpCopy);

w.r.t.

result CopyInto(BlkArray &replica) const {/**/}

and again when I call RemoveAll() on it which is a non-const method:

((BlkArray)backUpCopy).RemoveAll(true);

Is the first cast (shown above, (BlkArray&)) correct? It's the one aspect of indirection I haven't cast to before now. Then again I'll add another unused aspect, that of casting away constness for calling an object's methods, which the compiler isn't accepting as shown above.

The members are declared like this:

BlkArray fgBlocks;
const BlkArray backUpCopy;

I'm trying to extend Correa's solution so have:

    BlkArray *pBUCopy = (BlkArray *)&backUpCopy;
    fgBlocks.CopyInto(*pBUCopy);

Only problem now is the compiler is failing due to

uninitialized member 'MyClass::backUpCopy' with 'const' type 'const BlockArray'

John
  • 6,433
  • 7
  • 47
  • 82
  • Better to use `const_cast` rather than C-style casts. Makes it more obvious to the reader than constness is being cast away. – Wyzard Feb 28 '12 at 00:53

3 Answers3

9

Be aware that if you do this and the object really is const, then modifying it after casting away the constness is undefined behaviour.

fgBlocks.CopyInto(const_cast<BlkArray&>(backUpCopy));

Same thing for the other one:

const_cast<BlkArray&>(backUpCopy).RemoveAll(true);
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • The object really is `const`. Apart from the above calls, I made it a `const` member and is only otherwise used in a read-only capacity. The call to `RemoveAll` comes in the destructor of the containing class. – John Feb 28 '12 at 00:50
  • 1
    @John unfortunately you can't do what I posted then without causing UB. What you may want to do is make the real variable `private` and _not_ `const`, then make a `const` _reference_ to it `public`. This way, the class it is a member of can modify it, but the outside world can only look at it through the `const` reference and so cannot change it. – Seth Carnegie Feb 28 '12 at 00:51
  • @John: Why do you need to call `RemoveAll`? Just let `BlkArray`'s destructor deal with removing the data. – Xeo Feb 28 '12 at 00:54
  • I've got an accessor for that, I'm more concerned about this class, and classes which extend, it having write access other than for the two occassions defined above. – John Feb 28 '12 at 00:54
  • @Xeo: I'm afraid it's part of the two phase construction idiom, I need to supply `true` so it can deallocate its elements. – John Feb 28 '12 at 00:55
  • 4
    @John so you, the implementor, are trying to protect yourself from the implementation? – Seth Carnegie Feb 28 '12 at 00:57
  • @SethCarnegie: I'd just want an easier life 8) I might need to extend this class _very_ heavily and I recall being told that `const` was just "noise" when placed before parameters in the method declaration http://stackoverflow.com/a/9213503/866333 – John Feb 28 '12 at 01:00
  • @John: If it's a *value* parameter, that's an important distinction. Also, it's not *before*, it's *after*. `void f(int const value)` is the same as `void f(const int value)` and should look like that in the definition, while the declaration should just be `void f(int value)` – Xeo Feb 28 '12 at 01:14
  • @Xeo: I now appreciate it is less effort to omit `const` from declarations, but I _like_ to have my declarations accurately depict the actual definitions. – John Feb 28 '12 at 01:17
  • 2
    @John: An interface (declaration) should *not* tell you something about the implementation details (definition). – Xeo Feb 28 '12 at 01:21
  • @Xeo: I guess I fail to appreciate the distinction of value vs ref parameters. I use the latter so oftern I almost take it for granted that that is what I meant. Sorry, over-zealous migration from C to C++. – John Feb 28 '12 at 01:29
1

There is a little trick I learned looking at Qt's internals:

MyClass:circunventConst() const
{
    MyClass* that = const_cast<MyClass*>(this);
    that->myProtectedVariable = value;
}
George Green
  • 4,807
  • 5
  • 31
  • 45
Correa
  • 402
  • 2
  • 11
  • That's a few more lines than I'd hoped but seems right for me, thanks, I'll give that a go. – John Feb 28 '12 at 01:07
  • 1
    This doesn't help at all (if, as the poster said, `myProtectedVariable` is `const`, it is `const` whether or not `*this` is `const`). Also, this causes undefined behaviour if `*this` is really a `const` object. Qt is stupid silly for doing this. – Seth Carnegie Feb 28 '12 at 01:11
  • Well, I'm talking from memory, so can't give specifics why it was done or if it's still in Qt's code. But it sure was the first time I saw that. Of course, for this simple example, the proper way would be myProtectedVariable to be mutable so it would be valid for a const method to change it. It maybe that they saw bugs in compilers with mutable and were working around it, but then I'm just expeculating. – Correa Feb 28 '12 at 01:18
  • @SethCarnegie: Thanks man! I'll just give it a really stupid obscure name to keep it safe. – John Feb 28 '12 at 01:19
  • @SethCarnegie: So the purpose of `const_cast<>` is to make a pointer to `const` not. And it can only safely do that if the object pointed to is not declared `const`? – John Feb 28 '12 at 01:23
  • 2
    @John not only pointers, but references too. And your second sentence is almost but not quite correct: it can make a pointer or reference to an object that is declared `const` not `const` perfectly fine; the bad thing happens when you _modify_ the object that was declared `const` through the non-`const` pointer or reference you got from using `const_cast` to cast away the `const`-ness. _Then_ is when you get UB. The way to think of it is that `const_cast` casts away the `const`-ness of the type of the reference, but it doesn't actually make the underlying object non-`const`. – Seth Carnegie Feb 28 '12 at 01:25
  • @SethCarnegie: That explanation is very good. _Then_ being synonomous with _run-time_, as opposed to the gentle prodding the compiler is currently giving. – John Feb 28 '12 at 01:38
0

You can circumvent this problem by simply marking all methods as const, except RemoveAll and CopyFrom, the latter being made to be a method of BlkArray that either implements the logic or passes *this to CopyInto.

To be more secure about who can clear the data / copy new stuff into it, you may make those methods private and declare the necessary classes as friends, or use the passkey pattern to protect those two methods.

Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397