5sw
Someone said here const made the code too rigid. How so? Ok, if you want to change from const to non-const you will have to go up the call-graph and make more changes. But that's just a little bit of busy work. Then comes the real work of finding all the places that depend on the called function not changing the values and rewriting it so they can deal with that change.
Const does make the code too rigid. Const makes you go through way too many hoops to do things that are
trivial without const. I can't think of a single time where const has saved me time; it has only ever
cost me time. Also, your proclamation that changing things from const to non const is "just a little bit of busy work" is just shocking to me. I've had to do this before... I've had to change hundreds of instances in dozens of files just because of how infectious const is. This is not a little bit of busy work; it's untenable overhead. This is the kind of thing that makes programming a complete chore.
The biggest thing about const for me is that it ossifies the code prematurely or it simply causes busywork that's not necessary in any way. Say that I'm writing code that is heavily encapsulated and I want to make things "const correct":
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | class Foo
{
public:
Foo()
: m_i(0)
{
}
Foo(int i)
: m_i(i)
{
}
int GetInt() const
{
return m_i;
}
private:
int m_i;
};
|
We, of course, know that you can promote from non const to const trivially. So code like this is no problem:
| Foo f(54321);
const Foo *p = &f;
printf("%d\n", p->GetInt());
|
No problem, right? Let's start adding stuff to our code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 | class Foo
{
public:
Foo()
: m_i(0)
{
}
Foo(int i)
: m_i(i)
{
}
int GetInt() const
{
return m_i;
}
private:
int m_i;
};
class Entity
{
public:
Entity()
: m_name()
, m_health(0)
, m_foo(NULL)
{
}
void InitEntity(const char *name, int health, const Foo *foo)
{
snprintf(m_name, sizeof(m_name), "%s", name);
m_health = health;
m_foo = foo;
}
void UpdateAndPrint()
{
--m_health;
if (m_foo)
{
printf("%s has %d health and Foo %p -> %d\n", m_name, m_health, m_foo, m_foo->GetInt());
}
}
private:
char m_name[16];
int m_health;
const Foo *m_foo;
};
|
Still not too objectionable here, we're basically just inspecting values of Foo from Entity. This code, taken for just what it is in the current state doesn't seem very bad. But my experience tells me this is bad. Why? The code as it is now is just a snapshot of some process of evolution toward a final state. I can easily imagine a situation in the future where I might want to actually change the contents of Foo from Entity.
In the current state, this isn't possible for me to do unless I remove the const qualifiers and add in some non-const functions on Foo to let me change the values. You might say, "But that's not a lot of work! Just change it in the few instances you have." Yes, in this instance I have a few number of places to change. But I've worked in code bases before where literally just going from const to non-const might force me to go through dozens of files since the call chain might be so long/complicated.
Going back to the "sample" code, if I did in fact change the code to remove the const qualifiers so I can modify Foo from Entity (and also added a setter for m_i) then what have I accomplished? I just typed a bunch of code for utterly no reason and then removed it later because it was imposing a restriction on me that's absolutely not helpful.
I really don't understand what the big deal is with people wanting to know if values won't change. I used to care a lot about "know when values won't change, therefore use const everywhere", "the compiler can help me catch bugs!", but through the last 3-4 years of programming a game, I've come to the conclusion that in the vast majority of cases this is a complete waste of time. Simply put, I don't care anymore if values can be changed or not. The entire point of my program is to change values.
That isn't to say that it isn't useful to know when something will change or not. What I am saying though, is that annotating const wherever possible is a bad move because it limits the flexibility of your code and for future modification. I haven't come up with a good analogy, but the best I've come up with so far is being a construction contractor and you're about to set up to start building. You need to set up some scaffolding so you can build up your building. Const to me is setting up the scaffolding, but instead of easy to set up/tear down pipe scaffolding, it's steel I-beams you weld in place.
The analogy isn't quite right, but my point is that const forces me to make bets on code mutability that I can't guarantee. In games, especially, it's pretty much guaranteed that there's no code anywhere that I will write just once and never touch again.
It's hard for me to come up with a really concrete example that I can illustrate in a single forum post. Const has
massive costs that I can only describe through the process of writing code and evolving it. In fact, the keyword itself and what I do to the code are just complete opposites. Why should I use const at all if my code and data are always subject to change?
This is why I think using const is mostly a bad idea. Unless you live in an incredibly stable API boundary, the usage of const propagates incredible mental load and busy work in the process of
evolving code. Const and evolving, they're completely opposite. It's pointless to try to mix them.