Sign in to follow this  
Unwise owl

To bother or not to bother... "const" as function argument

Recommended Posts

Let's say you have a very simple function like this:
//would you write it like this...
void f(SSomeStruct& obj)
{
    std::cout << obj.x << std::endl;
}

//or like this:
void f(const SSomeStruct& obj)
{
    std::cout << obj.x << std::endl;
}

I know the latter example is more correct by definition, yet I never tend to use consts for function arguments. Why? Because they're just one more thing to think of. If I pass something by reference, I may suddenly realize I need to modify it, and then I must go back to the prototype. Either that, or typing "const " involves pressing 6 extra buttons on the keyboard for no immediate gain. Multiply this by the number of function arguments that should be constant in your program and you get into huge numbers quite quickly (then again, so is programming all other code already). I've seen professional code that ignores consts completely. I've seen posters at GameDev advoating why you shouldn't ignore them. Discuss!

Share this post


Link to post
Share on other sites
Since you passed the parameter by value, it's more or less pointless. It only really matter when you pass by reference, at which point you should make everything that can be const, const.

Typing 6 extra characters is nothing compared to having the compiler ensure the object really won't be (accidentally) modified.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
Since you passed the parameter by value, it's more or less pointless. It only really matter when you pass by reference, at which point you should make everything that can be const, const.

Typing 6 extra characters is nothing compared to having the compiler ensure the object really won't be (accidentally) modified.


Err you're right about that, I'll edit my example to accomodate for a better discussion...

Share this post


Link to post
Share on other sites
Quote:
I know the latter example is more correct by definition


Why? There's nothing more correct about that.

You might want to pass a smart pointer (NOT std::auto_ptr) or other ptr wrapper class by const value, but there's no reason you wouldn't pass that by const reference if you didn't want it altering.

Remember, const really exists for human benefit, no the compiler. It's a human guarentee that the funciton your calling won't logically alter your object.

There are plenty of good reason why a programmer might want to modify a value passed in a const (lazy evaluation for one).

Share this post


Link to post
Share on other sites
Quote:
Original post by Nitage
Quote:
I know the latter example is more correct by definition


Why? There's nothing more correct about that.

You might want to pass a smart pointer (NOT std::auto_ptr) or other ptr wrapper class by const value, but there's no reason you wouldn't pass that by const reference if you didn't want it altering.


Well yeah, but the question is when in fact it seems redundant. I.e, I've heard people argue in these forums before that consts should always be applied for objects that are not modified, regardless of how likely that is to happen. I guess it's a bit of a cooperative thing; in a team I guess preventing other to misuse code is a higher priority than in a one man project.

Share this post


Link to post
Share on other sites
Regarding your "no immediate gain" mention, const really shines when you make sure you use it properly in the whole program. If you just have a few const here and there, it will cause headaches - the "easy way out" being to remove the consts, which is a mistake: instead you should add more consts where they are needed. In the process of adding those const, either everything will fall into place, or you will find that you have a function that actually tried to modify the const entity. Which means that either the entity shouldn't have been const in the first place, or you've just found a nasty source of bugs.

Share this post


Link to post
Share on other sites
Quote:
Original post by Unwise owl I guess it's a bit of a cooperative thing; in a team I guess preventing other to misuse code is a higher priority than in a one man project.


Passing by const value doesn't provide any guards against misuse unless you're passing a class/struct which contains a non-const pointer/reference or static variable.

Share this post


Link to post
Share on other sites
Quote:
Original post by Nitage
Quote:
I know the latter example is more correct by definition


Why? There's nothing more correct about that.


The first function wouldn't be able to handle a const, literal, or unnamed temporary parameter. You're pointlessly restricting the kind of data it can deal with.

Quote:
Remember, const really exists for human benefit, no the compiler. It's a human guarentee that the funciton your calling won't logically alter your object.


I had a C inline function that took a parameter by value and manipulated it in ASM. GCC optimized the variable copying away when I declared that parameter to be const. No, in that case, I couldn't have passed it by reference. That function was performance-critical - we gained an extra 10% by just adding that keyword.

So, yes, in most cases, const is for humans, but it may also help the compiler optimize the code - it knows you're not going to change the contents of the 'constant variable' within the function.

Quote:
Original post by Nitage
Passing by const value doesn't provide any guards against misuse unless you're passing a class/struct which contains a non-const pointer/reference or static variable.


If a constant structure contains a pointer, then the pointer itself is constant, not the data it points to. What you say is true for a reference member, though. And a static variable is completely unaffected by the qualifications of any given instance. Passing by const value only means you won't be able to modify that value within the function. The original object it was copied from is unaffected in any case, const or no const.

Share this post


Link to post
Share on other sites
Quote:

Quote:
Quote:Original post by Nitage
Passing by const value doesn't provide any guards against misuse unless you're passing a class/struct which contains a non-const pointer/reference or static variable.




If a constant structure contains a pointer, then the pointer itself is constant, not the data it points to. What you say is true for a reference member, though. And a static variable is completely unaffected by the qualifications of any given instance. Passing by const value only means you won't be able to modify that value within the function. The original object it was copied from is unaffected in any case, const or no const.


Consider:

class ptrWrapper()
{
public:
int* get()const{return intPtr);
private:
int* intPtr;
};

void foo1(const ptrWrapper);
void foo2(const ptrWrapper&);
void foo3(ptrWrapper);



If const_cast isn't uses, foo1 and foo2 provide a guarentee the the original object passed in remains the same. foo3 doesn't. The original object may technically remain the same (the value of intPtr won't change, but the value of *intPtr will), but ny passing by value you probably intended for the value of *intPtr to remain the same.

My mistake with the static thing.

Share this post


Link to post
Share on other sites
Quote:
Original post by Unwise owl
Let's say you have a very simple function like this:

*** Source Snippet Removed ***

I know the latter example is more correct by definition, yet I never tend to use consts for function arguments. Why? Because they're just one more thing to think of. If I pass something by reference, I may suddenly realize I need to modify it, and then I must go back to the prototype. Either that, or typing "const " involves pressing 6 extra buttons on the keyboard for no immediate gain. Multiply this by the number of function arguments that should be constant in your program and you get into huge numbers quite quickly (then again, so is programming all other code already).

I've seen professional code that ignores consts completely. I've seen posters at GameDev advoating why you shouldn't ignore them. Discuss!


Use const when you should not when you can.
if the function doesn't change the object i also prefer to use this like:
//or like this:
void f(const SSomeStruct& obj) const
{
std::cout << obj.x << std::endl;
}
for 2 reason:
1- const parameter ensure that the object never change through this function
and it acts as a read-only argument so you can give access to critical objects
to friends using this style ( i mean friend accessor not boddies).
2- the second const optimise your function if you have a good compiler.

non of c++ elements are redundant ,all are good!
cheers

Share this post


Link to post
Share on other sites
Quote:
Original post by Nitage
If const_cast isn't uses, foo1 and foo2 provide a guarentee the the original object passed in remains the same. foo3 doesn't.


foo1 and foo3 both pass by value. The original object will not be modified.
Whether the local copy can be modified or not is irrelevant. Whether the object contains pointers or not is equally irrelevant.

Share this post


Link to post
Share on other sites
I think using const here is important for future readability. When I see void f(Whatever& w) in an interface, I assume that w will be modified by the function, so I take the necessary precautions in the caller. When I see void f(const Whatever& w), I can be sure the pass-by-reference is just for performance.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I'm sometimes lax on comments, and consting arguments helps me remember/work out what a function does.

Share this post


Link to post
Share on other sites
Here's an example of a possibly hard to find bug when passing by value

#include <iostream>
using namespace std;


void a(bool foo)
{
if (foo == true)
cout << "true" << endl;
else
cout << "false" << endl;
}

void b(bool foo)
{
if (foo = true)
cout << "true" << endl;
else
cout << "false" << endl;
}


int main()
{
a(true);
b(false);
return 0;
}








If foo had been const, this wouldn't compile (and the compiler would tell you where the problem is), but it compiled for me without warnings on g++ and outputs:
true
true

when you probably meant it to output:
true
false

Newbies tend to write code like this and it can happen pretty easily when you're really tired (or have been in front of the keyboard for more than 6 hours straight - more or less depending on the person). I usually think of the extra 6 keypresses as worthwile.

[Edited by - Will F on June 11, 2005 5:43:47 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Unwise owl
I've seen professional code that ignores consts completely.


The Doom 3 SDK apparently doesn't use the standard library, this doesn't necessarily mean you shouldn't.

While it seems like the extra 6 key presses add up, as your projects get larger and larger those key presses become less and less significant, and it can help you prevent some stupid bugs, especially when you're passing by reference.

Here's another Guru of the Week article you might like.

Share this post


Link to post
Share on other sites
My general experience is that I've been forced to go through and change pointer arguments to const too often, so it's a good habit that may save you from headaches later.

I haven't yet made a pointer argument const and then later needed to make it non-const.

Share this post


Link to post
Share on other sites
const correctness helps make our code 'more' type safe. this is part of the beauty of c++. the compiler can check for more then just fat fingered syntax errors. it can tell us when our design makes no sense. for example:

class X {
int i;
public:
void mutate() { ++i; }
};

class Y {
X m_x;
public:
const X & get_x () const { return m_x; }
// more code here
};

void foo ( X & x ) { x.mutate(); }

int main () {
Y y;
foo( y.get_x() ); // OOPS!
return 0;
}

let's say my algorithms in Y's other functions are under the assumption that no one else (other them Y itself) can mess with m_x. under this assumption i can make my code go faster (maybe i don't have to check the value of X::i as much, who knows). in the above code the compiler is going to tell me i'm breaking my own rule.

_ugly

Share this post


Link to post
Share on other sites
Here's a case direct from stuff I'm working on right now...

I took over ownership of a large body of code that has to support multithreading and be rock-solid. The code has evolved from single-threaded code over a period of 15 years or more. It's painfully clear by looking at it that multi-threading was added here and there as necessary, parts of it were well thought out for the conditions at the time and parts were just crammed in. As time went on the whole thing became a mess that was basically impossible to reason about as a whole. In particular everytime some multi-threading problem came up locks were arbitrarily inserted which solved the immediate problem but causing a wide variety of problems down the road.

My job (part of it anyway) is to clean up the mess. The first thing I did was go in and make large parts of the code const-correct. The resulting code churn is huge but mostly mechanical but more importantly is that I know what state of what objects need to be protected under what scenarios. I now have some reasonable assurance that I can change the locking to be sane and understandable by mere mortals. This would have been essentially impossible without const.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
The first function wouldn't be able to handle a const, literal, or unnamed temporary parameter. You're pointlessly restricting the kind of data it can deal with.


I'd like to re-iterate this point, since it can bite you. The problem is that non-const-correct code simply doesn't play well with const-correct code. Consider the following contrived example:


struct Vector3
{
Vector3( float X, float Y, float Z ) : x(X), y(Y), z(Z) {}
float x, y, z;
};

class Object
{
public:
const Vector3& GetPosition() const { return m_position; }
//....etc
private:
Vector3 m_position;
}

void Print( Vector3& v )
{
printf( "%f, %f, %f", v.x, v.y, v.z );
}

int main()
{
Vector3 pos( 1.0f, 2.0f, 3.0f );
Print( pos ); // This works fine

Print( Vector3( 1.0f, 2.0f, 3.0f ) ); // Oops! Compile error

Object obj;
Print( obj.GetPosition() ); // Oops! Compile error

return 0;
}



So basically, even if you don't care whether you functions modify the object or not, you will have problems if the other people you work with write const-correct code that needs to call your functions. In addition, you preclude yourself from being able to use temporaries.

-John

Share this post


Link to post
Share on other sites
Quote:
Original post by Teknofreek
So basically, even if you don't care whether you functions modify the object or not, you will have problems if the other people you work with write const-correct code that needs to call your functions. In addition, you preclude yourself from being able to use temporaries.

your point seems to be that you can't have a program or lib that is half-const-correct. this is of course a great point. i've also run into the problem of trying to keep my/our code const-correct while having to interface with libs that where not const correct. this is a serious pain is the ?!#. it's all or none i think. it takes additional dicipline. for me it allows the compiler to help my already overworked brain.

_ugly

Share this post


Link to post
Share on other sites
Quote:
Original post by mgarriss
Quote:
Original post by Teknofreek
So basically, even if you don't care whether you functions modify the object or not, you will have problems if the other people you work with write const-correct code that needs to call your functions. In addition, you preclude yourself from being able to use temporaries.

your point seems to be that you can't have a program or lib that is half-const-correct. this is of course a great point. i've also run into the problem of trying to keep my/our code const-correct while having to interface with libs that where not const correct. this is a serious pain is the ?!#. it's all or none i think. it takes additional dicipline. for me it allows the compiler to help my already overworked brain.

_ugly
Yet another reason why everyone should do it correctly.
btw that is what const_cast is for.

Guys, listen to Fruny he knows what he's talking about!

Do you want to be just some average joe programmer, or a top-notch programmer? All the experts do it, so why shouldn't you? Make your programs const-correct and people reading your code will have one more reason to think you're a top-notch programmer.

Share this post


Link to post
Share on other sites
another important thing to understand ... you do not make a thing const or not const based on whether or not is changes the object ....

you make it const or not const based on whether it PROMISES NOT TO CHANGE THE OBJECT or not ...

you add const when you are adding a restriction to the contract, saying in effect "under the rules of our current contract I promise not to mondify this object".

There are many functions that will not modify their object's in some cases, but will in others. For instance if your game world has mobile and immobile objects, the mobile object's will be modified when they colid with other objects, the immobile object's will not ... but the immobile object's collision handling functions still DO NOT use the const modifier on their parameter, because the contract for object's in general does not promise const-ness.

Now of course, while designing the program it may turn out that you will need to modify the object where you thought you would never need to (in which case, your initial contract is no longer valid). The reason const helps in these cases, is as a warning, hopefully causing the programmer who is about to remove it to consider carefully what they are doing, and to use "find in files" type analysis of client code to make sure such a design change is safe. If you had never used the const modifier in the first place, you wouldn't usually realize you are making a fundamental contract change in your object model.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this