'Should members usually not be const in C++ classes? The actual purpose of const members

At first, I was under the impression that member variables should usually be const in C++ classes, UNLESS i want it's member functions to modify those variables, internally for each object (the variables i usually put into private:).

but I now find out that i may have been wrong all along. e.g. i can't assign to such objects anymore. I can't even do move-assign.

Take say std::string. You can do this:

std::string foo("foo");
foo = std::string("bar");

this has to imply that internally, std::string has only non-const member variables right?

Is my thinking correct? Being relatively new to C++, it's new thinking is a bit strange. The purpose of const may not be what I thought it to be.

What's the ACTUAL PURPOSE of having const member variables then?

c++


Solution 1:[1]

I was under the impression that member variables should usually be const in C++ classes, UNLESS i want it's member functions to modify those variables

What you're forgetting is that the assignment operator, including the default one, is a member function. Example:

struct Foo {
    const int i = 0;

    Foo& operator =(const Foo& src)
    {
        if (&src == this)
            return *this;
        i = src.i;
        return *this;
    }
};

Since the assignment operator is a member function, it obviously has to be able to modify Foo::i. If not, you can't invoke it. Keep in mind that, given the Foo objects a and b, this:

a = b;

is actually syntactic sugar for this:

a.operator= (b);

Also keep in mind that the default assignment operator gets deleted in this case, because Foo::i is const. That means that Foo lacks an assignment operator altogether if you don't write one yourself.

Solution 2:[2]

const objects are objects you dont want to change, they stay constant during the execution of the program. You can assign a value to them in the member initialization list of the constructor of your class but not beyond that. If you have a variable that can have different values, you should not use const.

Solution 3:[3]

I would start by suggesting you look at the answer to this related question.

The fast and dirty answer is that const member properties contain values which remain unchangeable AFTER the object is initialized. The copy operations are attempting to preform an action which causes a new object to be constructed and THEN have the values from the old object assigned to the new object(in the implicit copy constructor body). What you most likely would want as a solution is to make an explicit copy constructor, which takes the original object as an argument and INITIALIZES the const property values in the initialization list.

Example of a copy contructor:

ClassWithConstantProperties( const ClassWithConstantProperties &orig) :
    constProperty1(orig.constProperty1)
    constProperty2(orig.constProperty2)
{
}

This tutorial explains more fully the implicitly defined constructors of classes in C++, and is also on an excellent C++ reference page.

Solution 4:[4]

It depends on the object, but in general, when you write the code for an object, you are responsible for its invariants. I rarely use const or reference members, but they are perfectly acceptable in classes which don't support assignment.

In general, const is used as logical const, not bitwise const. And const is more a contract between you and your clients, rather than something to protect you against yourself. In this sense, having const functions, returning const references, and taking const references as arguments makes a lot of sense. Top level const on an argument, or const in a class, doesn't really mean much, however.

Solution 5:[5]

Constant variables are used when the value should not be changed inside the class after construction.
Some examples: numerical constants (pi, e) and references.

struct Database_Record
{
    Database_Record(const std::string& table_name)
      : m_table_name(table_name)
    { ; }
    const std::string& get_table_name(void) const
    {  return m_table_name; }
private:
    const std::string m_table_name;
};

In the above structure representing a database record, it has a table name associated with the record, which cannot be change after intialization; this prevents the record from being written to the wrong table.

Solution 6:[6]

The data members of a class describe the state of an object of that type. A const data member implies that the objects have some state that never changes. This it not really very common. Often const members are also static.

The only thing you can do with a const data member is initialise it the member initialization list of a constructor.

The fact that you can assign to a std::string from another std::string implies only that its copy assignment operator does not do anything to its const data members (because of course it can't).

A class or struct that has a const data member will not have an implicitly defaulted copy assignment operator. The compiler can't assign to your const data member so it just says "If you want to assign, you'll have to define it yourself."

Solution 7:[7]

The C++ core guidelines (The C++ Core Guidelines are basically a set of tried-and-true guidelines, rules, and best practices about coding in C++ from the creators/maintainers of the language) says to not make data members const or references for exactly the problem you mentioned, that the class becomes non copy-assignable.

C++ Core guidelines: Don’t make data members const or references

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 fonZ
Solution 3 Community
Solution 4 James Kanze
Solution 5 Thomas Matthews
Solution 6
Solution 7 yasht