81

I'm really annoyed by const keyword these days, as I'm not quite familiar with it. I had a vector that stores all const pointers like vector<const BoxT<T> *> *Q_exclude, and in the constructor of another class, I need an element in this queue to be passed in as a parameter and assign it to a non-const member. My question is:

How do I assign a const variable to a non-const variable? I know this doesn't make sense because after all, a const is a const, and should not be changed by any mean. But that annoying member variable REALLY has to be changed during the process! I might also change the data type in the vector to be non-const, but that would be too much work. Or does anyone know how to avoid such situation?

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
Shang Wang
  • 24,909
  • 20
  • 73
  • 94
  • 7
    Why do you store pointers to const, if you want to change the objects they point to? – R. Martinho Fernandes Sep 05 '11 at 17:21
  • 1
    Maybe post some code... but as RMF says, this looks like unfortunate design. – Kerrek SB Sep 05 '11 at 17:23
  • "But that annoying member variable REALLY has to be changed during the process!" Then why did you make it a `const` member variable? If you find yourself needing to modify something that is `const`, then this indicate a design problem. Either you should not be modifying it, or you should rethink whether it is really `const` at all. – Nicol Bolas Sep 05 '11 at 17:26
  • There may be a bit of a problem with C++: you may want the data to be const in some functions and non-const in others. However, with `vector` it's either all or nothing. - Anyway, you don't need to make everything you can const. IMO, if you can pass const references (for performance), that is enough. – UncleBens Sep 05 '11 at 17:39
  • 1
    @UncleBens: Store non-`const` elements, then pass the entire vector as `const` into the contexts where element access should not be mutable. Simples! – Lightness Races in Orbit Sep 05 '11 at 20:46
  • @Tomalak: not quite, because a `const vector` still lets you modify the referands of its elements. The caller might agree a convention with you, that a const vector of pointers means "don't modify the referands either", but the type system isn't in on the deal. You could use an adaptor for the purpose (if you pass ranges as iterator pairs, rather than passing the whole vector, it's not that much work to write the adaptor). – Steve Jessop Sep 05 '11 at 21:31
  • @Steve: Ah, yes, because you get a `T* const`. I'm with you now. Well this isn't so much of a design flaw as a fact of indirection that some people don't like to accept. :) Besides, storing pointers in a `vector` is weak. – Lightness Races in Orbit Sep 05 '11 at 21:47
  • @Tomalak: What I meant by all or nothing is what you suggested. What if I want a function to be able to modify the container, but not existing items? - I suspect this cannot be really achieved, so down with `const`. It's convenient when the language allows it nicely, but other languages do nicely without const, so you'll be fine without it too. – UncleBens Sep 06 '11 at 17:24
  • @UncleBens: You could store `T const*` (which is not the same as `T* const`). But yea, then you can never modify existing elements. Frankly I think this "all or nothing"ness makes sense rather than being "a bit of a problem with C++"; it should be up to the container to decide the mutability of stuff inside it, not some arbitrary function that wants to use it. – Lightness Races in Orbit Sep 06 '11 at 21:04

5 Answers5

138

You can assign a const object to a non-const object just fine. Because you're copying and thus creating a new object, constness is not violated.

Like so:

int main() {
   const int a = 3;
   int b = a;
}

It's different if you want to obtain a pointer or reference to the original, const object:

int main() {
   const int a = 3;
   int& b = a;       // or int* b = &a;
}

//  error: invalid initialization of reference of type 'int&' from
//         expression of type 'const int'

You can use const_cast to hack around the type safety if you really must, but recall that you're doing exactly that: getting rid of the type safety. It's still undefined to modify a through b in the below example:

int main() {
   const int a = 3;
   int& b = const_cast<int&>(a);

   b = 3;
}

Although it compiles without errors, anything can happen including opening a black hole or transferring all your hard-earned savings into my bank account.

If you have arrived at what you think is a requirement to do this, I'd urgently revisit your design because something is very wrong with it.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 22
    +1 for saying that _anything can happen including opening a black hole or transferring all your hard-earned savings into my bank account_ . Excellent answer as always !!! – Destructor May 15 '16 at 19:31
  • 16
    @Destructor: It should be noted that, despite the prevalence on Stack Overflow of code with UB, nobody's hard-earned savings have yet appeared in my bank account. :( – Lightness Races in Orbit May 15 '16 at 19:37
13

Changing a constant type will lead to an Undefined Behavior.

However, if you have an originally non-const object which is pointed to by a pointer-to-const or referenced by a reference-to-const then you can use const_cast to get rid of that const-ness.

Casting away constness is considered evil and should not be avoided. You should consider changing the type of the pointers you use in vector to non-const if you want to modify the data through it.

2NinerRomeo
  • 2,687
  • 4
  • 29
  • 35
Alok Save
  • 202,538
  • 53
  • 430
  • 533
7

The actual code to cast away the const-ness of your pointer would be:

BoxT<T> * nonConstObj = const_cast<BoxT<T> *>(constObj);

But note that this really is cheating. A better solution would either be to figure out why you want to modify a const object, and redesign your code so you don't have to.... or remove the const declaration from your vector, if it turns out you don't really want those items to be read-only after all.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • You don't get a "non const object" at all. You get a variable with a non-const type, but the object to which it acts as a handle _is still immutable_. – Lightness Races in Orbit Sep 05 '11 at 17:50
  • 1
    @Tomalak: if it was immutable before, that is. If it was mutable before, then it's still mutable *and* you've got a means to modify it, which is how such const-cast shenanigans become tempting. – Steve Jessop Sep 05 '11 at 19:18
  • Had to do this because of the way Unreal deals with reference types in Blueprint functions. In Unreal, a non-const reference parameter is considered an out parameter. Because of this and the fact that pointers don't work on structs in Blueprints, you have to use const references if you want a reference as an input parameter. – Dynamiquel Jan 27 '22 at 16:11
3

Leaving this here for myself,

If I get this error, I probably used const char* when I should be using char* const.

This makes the pointer constant, and not the contents of the string.

const char* const makes it so the value and the pointer is constant also.

1
void SomeClass::changeASettingAndCallAFunction() const {
    someSetting = 0; //Can't do this
    someFunctionThatUsesTheSetting();
}

Another solution is to call said function in-between making edits to variables that the const function uses. This idea was what solved my problem being as I was not inclined to change the signature of the function and had to use the "changeASettingAndCallAFunction" method as a mediator:

When you call the function you can first make edits to the setting before the call, or (if you aren't inclined to mess with the invoking place) perhaps call the function where you need the change to the variable to be propagated (like in my case).

void SomeClass::someFunctionThatUsesTheSetting() const {
     //We really don't want to touch this functions implementation
     ClassUsesSetting* classUsesSetting = ClassUsesSetting::PropagateAcrossClass(someSetting);
     /*
         Do important stuff
     */
}

void SomeClass::changeASettingAndCallAFunction() const {
     someFunctionThatUsesTheSetting();
     /*
         Have to do this
     */
}

void SomeClass::nonConstInvoker(){
    someSetting = 0;
    changeASettingAndCallAFunction();
}

Now, when some reference to "someFunctionThatUsesTheSetting" is invoked, it will invoke with the change to someSetting.

djg
  • 131
  • 9