Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const_cast a const member in a class constructor

I sometimes use const_cast when I want a member variable of a class to be constant during the life of the class, but it needs to be mutable during the constructor. Example:

struct qqq {
 const vector<foo> my_foo;

  qqq(vector<foo>* other) {
    vector<foo>& mutable_foo = const_cast<vector<foo>&>(my_foo)
    other->swap(mutable_foo);
  }
};

I had assumed that doing this in the constructor was basically OK because nobody else is relying on it at this point so it wouldn't interact badly with optimization, etc.

However recently someone told me this is "undefined behavior" and that it's basically illegal to mutate a const object after it's been constructed under any circumstance.

Can someone clarify? Is this a bad / undefined behavior / thing to do?

like image 848
drwowe Avatar asked Apr 18 '13 17:04

drwowe


People also ask

Can const be initialized in constructor?

To initialize the const value using constructor, we have to use the initialize list. This initializer list is used to initialize the data member of a class. The list of members, that will be initialized, will be present after the constructor after colon. members will be separated using comma.

What is the use of const_cast in C++?

const_cast is one of the type casting operators. It is used to change the constant value of any object or we can say it is used to remove the constant nature of any object. const_cast can be used in programs that have any object with some constant value which need to be changed occasionally at some point.

How do I remove a const?

The statement int* c = const_cast<int>(b) returns a pointer c that refers to a without the const qualification of a . This process of using const_cast to remove the const qualification of an object is called casting away constness.

Can you cast a const in C++?

const_cast is used to cast away the constness of variables. Following are some interesting facts about const_cast. 1) const_cast can be used to change non-const class members inside a const member function.


2 Answers

It is Undefined Behavior. Per Paragraph 7.1.6.1/4 of the C++11 Standard:

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.

In this case, it seems like you want your object to "become" constant after construction. This is not possible.

If your vector is meant to be const, you shall initialize it in the constructor's initialization list:

qqq(vector<foo>& other) 
    : my_foo(std::move(other)) 
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}

Notice, that unless you have a good reason for passing by pointer - in which case, you should also be checking whether the pointer is non-null - you should consider passing by reference (as shown above), which is the common practice.

UPDATE:

As Pete Becker correctly points out in the comments, proper design would suggest that the decision to move from the vector argument should belong to the caller of qqq's constructor, and not to the constructor itself.

If the constructor is always supposed to move from its argument, then you could let it accept an rvalue reference, making it clear what the constructor itself is expecting out of the caller:

qqq(vector<foo>&& other) 
//             ^^
    : my_foo(std::move(other)) 
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}

This way, the caller would have to provide an rvalue in input to qqq's constructor:

std::vector<foo> v;
// ...
qqq q1(v); // ERROR!
qqq q2(std::move(v)); // OK! Now the client is aware that v must be moved from
like image 102
Andy Prowl Avatar answered Oct 05 '22 23:10

Andy Prowl


Yes, it's indeed UB (Undefined Behaviour). You cannot modify a const object once it's initialised. What you should do is use the member initialiser list, perhaps together with a function:

struct qqq {
  const vector<foo> my_foo;

  qqq(vector<foo> *other) : my_foo(initialiseFoo(*other)) {}

  static vector<foo> initialiseFoo(vector<foo> &other) {
    vector<foo> tmp;
    other.swap(tmp);
    return tmp;
  }
};

A decent optimiser should be able to get rid of the temporary.

If you can use C++11, it's actually even simpler:

struct qqq {
  const vector<foo> my_foo;

  qqq(vector<foo> *other) : my_foo(std::move(*other))
  {
    other->clear();  //Just in case the implementation of moving vectors is really weird
  }
};
like image 30
Angew is no longer proud of SO Avatar answered Oct 06 '22 00:10

Angew is no longer proud of SO