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 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 on other sites
Quote:
 Original post by FrunySince 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 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 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 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 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 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 NitagePassing 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 on other sites
Quote:

Quote:
 Quote:Original post by NitagePassing 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 on other sites
we use them for the same sort of reason we use public/private labels.

Share on other sites
and a compiler also optimizes when it sees const, btw

Share on other sites
Quote:
 Original post by Genjixand a compiler also optimizes when it sees const, btw

Not very often does optimization occur from the use of const. See GOTW for reasons why.

Share on other sites
Quote:
 Original post by Unwise owlLet'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
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 on other sites
Quote:
 Original post by NitageIf 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 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 on other sites
I'm sometimes lax on comments, and consting arguments helps me remember/work out what a function does.

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 on other sites
Quote:
 Original post by Unwise owlI'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 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 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 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 on other sites
Quote:
 Original post by FrunyThe 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 on other sites
Quote:
 Original post by TeknofreekSo 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 on other sites
Quote:
Original post by mgarriss
Quote:
 Original post by TeknofreekSo 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 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.

Create an account

Register a new account

• Forum Statistics

• Total Topics
628327
• Total Posts
2982091

• 22
• 9
• 9
• 13
• 11