Say I have the following member function:
void CFoo::regWrite( int addr, int data )
{
reg_write( addr, data ); // driver call to e.g. write a firmware register
}
Clearly, calling this function doesn't modify the internal state of the object it is called on. However, it changes the state of whatever this Foo instance represents.
In circumstances such as these, should Foo::regWrite(int addr, int data) be a const function?
You have to decide what the meaning is of "logically const" for the class CFoo, and that depends what the class is for.
If CFoo is construed as referring to some data, then it might make sense to be able to modify that data via a const instance of CFoo, in which case your member function would be const. For examples of this consider other types that refer to some data -- you can modify the referand of a char *const or a const std::unique_ptr<char>.
If CFoo is construed as owning some data, then it might make sense to forbid modification via a const instance of CFoo. For examples of this consider containers, where the elements are logically "part of the object's state" even when they aren't physically part of the object. So vector::operator[] has a const overload that returns a const T& rather than a T&, the insert member function is non-const, etc.
It is up to the programmer to define what 'const' shall mean for a class. With the specifier mutable you can even have a constobject with changing values in a member. When it comes to hardware one might consider the configuration as the target for const correctness: as long as the configuration does not change the object can be considered constant.
A similar issue rises if you have pointers to other objects in your class: Your const method can then call non-const methods on the other object and thus modifiy it.
If you look at the hardware as as some other object referenced by your class, it would be perfectly valid to modify firmware settings (since only a "referenced" object is changed). If you want your class to "represent" the hardware (or part of it), I would rather suggest not to mark the method as const.
So I think it mainly depends how you designed your class.
There are two ways of looking at this - the optimization angle, and the logic of this declaration. Which is more important is for you to decide.
Optimization
EDIT: I made some incorrect assumptions. It seems the compiler is not actually free to make the optimizations below, and will only make them by analyzing the body of the method to ensure no modifications occur (and even then only in simple cases).
Having this const will allow the compiler to optimize a little bit
more. It knows that regWrite doesn't change any fields in the
object, so it can keep them if it was storing them in registers, and
do similar optimizations that rely on the objects fields not being
changed.
This is really the only thing the compiler will depend on when you
make a definition like this, so having this const is OK and can
theoretically allow better performance.
Making logical sense
It feels unintuitive to have a const method whose whole purpose is a destructive change. The usual intuition a programmer has is that as long as I'm only calling const methods, the results of other const methods shouldn't change. If you violate this unwritten contract, expect people to be surprised - even if the compiler is OK with it.
I'm not sure if this will be violated here - it will depend on the other code in this class. However, if no other considerations are important (performance, etc.), const is (for me) mostly a marker on the interface which says "calling this does not change the state of this object", for a broad definition of "state".
This is murky ground however, and it is up to you what you consider a state change. If you think of your firmware object as representing a link to the internals, writing a register does not change anything about this link and is const. If you think of it as representing the state of the underlying registers, than writing to registers is a change of state.
Related
Is it a good practice, in C++, to add const at the end of a member function definition every time the function does not modify the object, i.e., every time the function is 'eligible' for const?
I know that it's necessary in this case:
class MyClass {
public:
int getData() const;
};
void function(const MyClass &m) { int a = m.getData(); dosomething... }
but other than this, and other uses of const for actual functionality, does adding const to the end actually change the way code is executed (faster/slower) or is it just a 'flag' for the compiler to handle cases such as the one above?
In other words, if const (at the end) is not needed for functionality in a class, does adding it make any difference?
Please see this excellent article about const correctness by Herb Sutter (C++ standards committee secretary for 10 years.)
Regarding optimizations, he later wrote this article where he states that "const is mainly for humans, rather than for compilers and optimizers." Optimizations are impossible because "too many things could go wrong...[your function] might perform const_casts."
However, const correctness is a good idea for two reasons: It is a cheap (in terms of your time) assertion that can find bugs; and, it signals the intention that a function should theoretically not modify the object's external state, which makes code easier to understand.
every time the function does not modify the object, i.e., every time the function is 'eligible' for const?
In my opinion, Yes. It ensures that you call such functions on const objects or const expressions involving the object:
void f(const A & a)
{
a.inspect(); //inspect must be a const member function.
}
Even if it modifies one or few internal variables once or twice, even then I usually make it const member function. And those variables are declared with mutable keyword:
class A
{
mutable bool initialized_cache; //mutable is must!
public:
void inspect() const //const member function
{
if ( !initialized_cache)
{
initialized_cache= true;
//todo: initialize cache here
}
//other code
}
};
Yes. In general, every function that is logically const should be made const. The only gray areas are where you modify a member through a pointer (where it can be made const but arguably should not be const) or where you modify a member that is used to cache a computation but otherwise has no effect (which arguably should be made const, but will require the use of the keyword mutable to do so).
The reason why it's incredibly important to use the word const is:
It is important documentation to other developers. Developers will assume that anything marked const does not mutate the object (which is why it might not be a good idea to use const when mutating state through a pointer object), and will assume that anything not marked const mutates.
It will cause the compiler to catch unintentional mutations (by causing an error if a function marked const unintintionally calls a non-const function or mutates an element).
Yes, it is a good practice.
At the software engineering level it allows you to have read-only objects, e.g. you can prevent objects from being modified by making them const. And if an object is const, you are only allowed to call const functions on it.
Furthermore, I believe the compiler can make certain optimizations if it he knows that an object will only be read (e.g., share common data between several instances of the object as we know they are never being modified).
The 'const' system is one of the really messy features of C++. It is simple in concept, variables declared with ‘const’ added become constants and cannot be altered by the program, but, in the way is has to be used to bodge in a substitute for one of the missing features of C++, it gets horridly complicated and frustratingly restrictive. The following attempts to explain how 'const' is used and why it exists. Of the mixes of pointers and ‘const’, the constant pointer to a variable is useful for storage that can be changed in value but not moved in memory and the pointer (constant or otherwise) is useful for returning constant strings and arrays from functions which, because they are implemented as pointers, the program could otherwise try to alter and crash. Instead of a difficult to track down crash, the attempt to alter unalterable values will be detected during compilation.
For example, if a function which returns a fixed ‘Some text’ string is written like
char *Function1()
{ return “Some text”;}
then the program could crash if it accidentally tried to alter the value doing
Function1()[1]=’a’;
whereas the compiler would have spotted the error if the original function had been written
const char *Function1()
{ return "Some text";}
because the compiler would then know that the value was unalterable. (Of course, the compiler could theoretically have worked that out anyway but C is not that clever.)
When a subroutine or function is called with parameters, variables passed as the parameters might be read from to transfer data into the subroutine/function, written to to transfer data back to the calling program or both to do both. Some languages enable one to specify this directly, such as having ‘in:’, ‘out:’ & ‘inout:’ parameter types, whereas in C one has to work at a lower level and specify the method for passing the variables choosing one that also allows the desired data transfer direction.
For example, a subroutine like
void Subroutine1(int Parameter1)
{ printf("%d",Parameter1);}
accepts the parameter passed to it in the default C & C++ way which is a copy. Therefore the subroutine can read the value of the variable passed to it but not alter it because any alterations it makes are only made to the copy and lost when the subroutine ends so
void Subroutine2(int Parameter1)
{ Parameter1=96;}
would leave the variable it was called with unchanged not set to 96.
The addition of an ‘&’ to the parameter name in C++ (which was a very confusing choice of symbol because an ‘&’ infront of variables elsewhere in C generate pointers!) like causes the actual variable itself, rather than a copy, to be used as the parameter in the subroutine and therefore can be written to thereby passing data back out the subroutine. Therefore
void Subroutine3(int &Parameter1)
{ Parameter1=96;}
would set the variable it was called with to 96. This method of passing a variable as itself rather than a copy is called a ‘reference’ in C.
That way of passing variables was a C++ addition to C. To pass an alterable variable in original C, a rather involved method using a pointer to the variable as the parameter then altering what it pointed to was used. For example
void Subroutine4(int *Parameter1)
{ *Parameter1=96;}
works but requires the every use of the variable in the called routine so altered and the calling routine altered to pass a pointer to the variable which is rather cumbersome.
But where does ‘const’ come into this? Well, there is a second common use for passing data by reference or pointer instead of copy. That is when copying a the variable would waste too much memory or take too long. This is particularly likely with large compound user-defined variable types (‘structures’ in C & ‘classes’ in C++). So a subroutine declared
void Subroutine4(big_structure_type &Parameter1);
might being using ‘&’ because it is going to alter the variable passed to it or it might just be to save copying time and there is no way to tell which it is if the function is compiled in someone else’s library. This could be a risk if one needs to trust the the subroutine not to alter the variable.
To solve this, ‘const’ can be used the in the parameter list like
void Subroutine4(big_structure_type const &Parameter1);
which will cause the variable to passed without copying but stop it from then being altered. This is messy because it is essentially making an in-only variable passing method from a both-ways variable passing method which was itself made from an in-only variable passing method just to trick the compiler into doing some optimization.
Ideally, the programmer should not need control this detail of specifying exactly how it variables are passed, just say which direction the information goes and leave the compiler to optimize it automatically, but C was designed for raw low-level programming on far less powerful computers than are standard these days so the programmer has to do it explicitly.
My understanding is that it is indeed just a flag. However, that said, you want to add it wherever you can. If you fail to add it, and a function elsewhere in your code does something like
void function(const MyClass& foo)
{
foo.getData();
}
You will run into issues, for the compiler cannot guarantee that getData does not modify foo.
Making member functions const ensures that calling code that has const objects can still call the function. It is about this compiler check - which helps create robust self-documenting code and avoid accidental modifications of objects - and not about run-time performance. So yes, you should always add const if the nature of the function is such that it doesn't need to modify the observable value of the object (it can still modify member variables explicitly prefixed with the mutable keyword, which is intended for some quirky uses like internal caches and counters that don't affect the client-visible behaviour of the object).
I'm writing a class that wraps a legacy C API that controls a hardware device. In a simplified example, I might have something like:
class device
{
public:
void set_request(int data) { legacy_set_req(p_device, data); }
int get_response() const { return legacy_get_rsp(p_device); }
private:
device_handle_t *const p_device;
};
The class itself has no bitwise state; therefore, I could choose to declare set_request() as const, and the compiler would be happy with that. However, from a semantic point-of-view, would this be the correct approach, given that it affects the observable behaviour of the object? (i.e. the encapsulated hardware device very much does have state.)
I believe that const should reflect logical const-ness, regardless of the internal representation. Just because your object contains only a pointer to something that changes, doesn't mean all your member functions should be const.
C++ even has the mutable concept for internal representation that needs to change even if conceptually the object does not. The const keyword is clearly not intended to represent "bitwise" const-ness.
If it changes the state, it generally should not be const. The fact that the state in question is owned remotely (i.e., in a controlled device) doesn't change that.
A useful test is:
could code with only const access to the device instance interfere with the operation of code with non-const access
Basically, const access is meant to mean you're effectively an observer. Here, it looks as though code calling set_request(...) at time T1 then get_response() at time T3 would be seriously screwed if some supposed observer called set_request() with different parameters at time T2. For that reason, set_request(...) shouldn't be accessible to observers - it should be non-const.
(There are caveats to the test - e.g. if a const "observer" needs a lock it can obviously affect non-const usage from another thread from a timing perspective but shouldn't do so from a functional one - but it's pretty obvious how to factor that into your analysis).
Like other type and member qualifications (e.g., public, private, virtual), const expresses both intention and the language semantics (i.e., safety features) that support that intention. In this case, the intention would appear counter-intuitive, even if the underlying semantics would be safe. I wouldn't do it.
After reading this, it is my understanding that declaring a method as const prevents it from accidentally modifying the class's member variables.
Are const methods commonly used?
Should they be used for everything that shouldn't modify member variables?
Yes, const should always be used, when appropriate.
It lets your compiler check your application logic, statically asserting const-correctness for free!
Some people even say that const should be the default, and you should be forced to use mutable for what is non-constant.
I use const extensively to communicate design intent. If I intended that a method was a pure query, and not a modification function, I would both enforce that and communicate it with a 'const' in the signature.
I encourage you to look at Meyer's thoughts on the matter of side-effect-free functions, and the implications that has on enabling testing.
Just my testimony.
Some years ago, I was still against the use of const, just because of the constraints in design and writing longer function signatures... and so on...
But one of my project leaders always insisted, all the time, reminding me: "You should use const function, it avoids accidents and doing non-sense".
And one day I faced an impossible bug to find. Days after days after days... A nightmare. The design was too big for me too see so as I could grasp it in its whole. I searched in vain until I decided I was lost.
Then I spent two days redefining ALL the functions that should be const. I mean it, two days. (Recompilations were long as it was a 5 millions lines of code project).
And then: simply I found the bug... rather the Compiler found the bug for me: In a getter-like method, which should have given me the prefered size of a gui control, the code was actually computing the size, but it was also caching its size and updating its size... Thus modifying the object.
Now, sometimes I forget to put const. But if I notice it, I correct it.
Adding const to a member function allows it to be called on const references to an object, because it guarantees that instance variables won't be changed. Const references occur in various places throughout the STL, so it makes sense to mark member functions as const where the function doesn't intend to modify the state of an object.
Note: it is possible to mark certain instance variables as mutable so they can be changed even by const functions. This is useful to implement look-up caches, for example.
Declaring a method that shouldn't modify member variables:
Assures that what you think is what is happening, i.e. that you're not accidentally modifying a variable somewhere.
Declares to callers of the function that this method doesn't modify member variables, removing the need to read over the code or rely on documentation that says so.
So yes, use const wherever it makes sense. They're not as widely used as I'd like to see though, most likely because the majority of developers don't see the huge benefit.
If you forget to mark an accessor as const, the compiler will not allow the method to be called on const objects or references to const objects. So yes, marking accessors as const is important.
If you have a const reference or pointer (i.e. pointer to const) of a class object then you can ONLY call const member methods of the class.
So if someone "forgot" to make a "get" method const, you would not be able to call it with a const reference (There is a workaround with const_cast but we don't want to use that!).
So yes, if the class is not going to be modified by the method then it should be const.
Note: there are some occasions that you do want to modify a variable as an "implementation detail", eg lazy-loading or to lock a mutex. In such a case you can still make the method const but make that member variable "mutable".
If you are writing a virtual method, it should be const if no derived class will need it to be mutable.
You should use the const keyword whenever possible.
It prevent you from the mistakes in the code.
It increase the readability of the code very much.
Everyone who is reading the header and seeing const keywords can immediately understand that a const method does not change the state of an object and can be used with no being afraid he will change the object for example
About six years ago, a software engineer named Harri Porten wrote this article, asking the question, "When should a member function have a const qualifier and when shouldn't it?" I found it to be the best write-up I could find of the issue, which I've been wrestling with more recently and which I think is not well covered in most discussions I've found on const correctness. Since a software information-sharing site as powerful as SO didn't exist back then, I'd like to resurrect the question here.
The article seems to cover a lot of basic ground, but the author still has a question about const and non-const overloads of functions returning pointers. Last line of the article is:
Many will probably answer "It depends." but I'd like to ask "It depends on what?"
To be absolutely precise, it depends whether the state of the A object pointee is logically part of the state of this object.
For an example where it is, vector<int>::operator[] returns a reference to an int. The int referand is "part of" the vector, although it isn't actually a data member. So the const-overload idiom applies: change an element and you've changed the vector.
For an example where it isn't, consider shared_ptr. This has the member function T * operator->() const;, because it makes logical sense to have a const smart pointer to a non-const object. The referand is not part of the smart pointer: modifying it does not change the smart pointer. So the question of whether you can "reseat" a smart pointer to refer to a different object is independent of whether or not the referand is const.
I don't think I can provide any complete guidelines to let you decide whether the pointee is logically part of the object or not. However, if modifying the pointee changes the return values or other behaviour of any member functions of this, and especially if the pointee participates in operator==, then chances are it is logically part of this object.
I would err on the side of assuming it is part (and provide overloads). Then if a situation arose where the compiler complains that I'm trying to modify the A object returned from a const object, I'd consider whether I really should be doing that or not, and if so change the design so that only the pointer-to-A is conceptually part of the object's state, not the A itself. This of course requires ensuring that modifying the A doesn't do anything that breaks the expected behaviour of this const object.
If you're publishing the interface you may have to figure this out in advance, but in practice going back from the const overloads to the const-function-returning-non-const-pointer is unlikely to break client code. Anyway, by the time you publish an interface you hopefully have used it a bit, and probably got a feel for what the state of your object really includes.
Btw, I also try to err on the side of not providing pointer/reference accessors, especially modifiable ones. That's really a separate issue (Law of Demeter and all that), but the more times you can replace:
A *getA();
const A *getA() const;
with:
A getA() const; // or const A &getA() const; to avoid a copy
void setA(const A &a);
The less times you have to worry about the issue. Of course the latter has its own limitations.
One interesting rule of thumb I found while researching this came from here:
A good rule of thumb for LogicalConst is as follows: If an operation preserves LogicalConstness, then if the old state and the new state are compared with the EqualityOperator, the result should be true. In other words, the EqualityOperator should reflect the logical state of the object.
I personally use a very simple Rule Of Thumb:
If the observable state of an object does not change when calling a given method, this method ought to be const.
In general it is similar to the rule mentioned by SCFrench about Equality Comparison, except that most of my classes cannot be compared.
I would like to push the debate one step further though:
When requiring an argument, a function ought to take it by const handle (or copy) if the argument is left unchanged (for an external observer)
It is slightly more general, since after all the method of a class is nothing else than a free-standing function accepting an instance of the class as a first argument:
class Foo { void bar() const; };
is equivalent to:
class Foo { friend void bar(const Foo& self); }; // ALA Python
when it doesn't modify the object.
It simply makes this to have type const myclass*. This guarantees a calling function that the object won't change. Allowing some optimizations to the compiler, and easier for the programmer to know if he can call it without side effects (at least effects to the object).
General rule:
A member function should be const if it both compiles when marked as const and if it would still compile if const were transitive w.r.t pointers.
Exceptions:
Logically const operations; methods that alter internal state but that alteration is not detectable using the class's interface. Splay tree queries for example.
Methods where the const/non-const implementations differ only by return type (common with methods the return iterator/const_iterator). Calling the non-const version in the const version via a const_cast is acceptable to avoid repetition.
Methods interfacing to 3rd party C++ that isn't const correct, or to code written in a language that doesn't support const
Here are some good articles:
Herb Sutter's GotW #6
Herb Sutter & const for optimizations
More advice on const correctness
From Wikipedia
I use const method qualifiers when the method does not alter the class' data members or its common intent is not to modify the data members. One example involves RAII for a getter method that may have to initialize a data members (such as retrieve from a database). In this example, the method only modifies the data member(s) once during initialization; all other times it is constant.
I'm allowing the compiler to catch const errors during compile time rather than me catching them during run-time (or a User).
I'm adding some lazy initialization logic to a const method, which makes the method in fact not const. Is there a way for me to do this without having to remove the "const" from the public interface?
int MyClass::GetSomeInt() const
{
// lazy logic
if (m_bFirstTime)
{
m_bFirstTime = false;
Do something once
}
return some int...
}
EDIT: Does the "mutable" keyword play a role here?
Make m_bFirstTime mutable:
class MyClass
{
: :
mutable bool m_bFirstTime;
};
...but this is also very often an indication of a design flaw. So beware.
Actually, you said that you didn't want to change the header file. So your only option is to cast away the constness of the this pointer...
int MyClass::GetSomeInt() const
{
MyClass* that = const_cast<MyClass*>(this);
// lazy logic
if (that->m_bFirstTime)
{
that->m_bFirstTime = false;
Do something once
}
return some int...
}
If using mutable raises a red flag, this launches a red flag store in to orbit. Doing stuff like this is usually a really bad idea.
I think of this problem as involving two concepts: (1) "logically const" and (2) "bitwise const". By this I mean that getting some int from a class, does not logically change the class and in most cases it does not change the bits of the class members. However, in some cases, like yours, it does.
In these cases, where the method is logically const but not bitwise const, the compiler cannot know this. This is the reason for the existence of the mutable keyword. Use it as John Dibling shows, but it is not a design flaw. On the contrary, there are many cases where this is necessary. In your example, I presume that the calculation of the int is expensive, so we do not want to calculate it if it is not needed. In other cases, you may wish to cache results of methods for later use, etc.
BTW, even though you have accepted the "mutable" answer as correct, you do have to update the .h!
set the m_bFirstTime member to be mutable
As John Dibling said, mark the fields that are changed as mutable. The important part is in the comment by ypnos: 'don't really change the state of the object' (as perceived by the outside world). That is, any method call before and after the const method call must yield the same results. Else your design is flawed.
Some things that make sense to be mutable:
mutex or other lock types
cached results (that will not change)
Mutex are not part of your objects state, they are only a blocking mechanism to guarantee data integrity. A method that will retrieve a value from your class, does need to change the mutex, but your class data and state will be exactly the same after the execution of the const method as it was before.
With caching, you must consider that only data that it makes sense for data that is expensive to retrieve and assumed not to change (DNS result, as an example). Else you could be returning stale data to your user.
Some things that should not be changed inside const methods:
Anything that modifies the state of
the object
Anything that affects this or other
method results
Any user of your class that executes const methods will assume that your class (as seen from the outside world) will not change during the execution. It will be quite misleading and error prone if it were not the same. As an example, assume that a dump() method changes some internal variable -state, value- and that during debug the user of your class decides to dump() your object at a given point: your class will behave differently with traces than without: perfect debug nightmare.
Note that if you do lazy optimizations you must do them to access immutable data. That is, if your interface dictates that during construction you will retrieve an element from the database that can be later accessed through a constant method, then if you do lazy fetching of the data you can end up in a situation where the user constructs your object to keep a copy of the old data, modifies the database and later on decides to restore the previous data into the database. If you have performed a lazy fetch you will end up loosing the original value. An opposite example would be configuration file parsing if the config file is not allowed to be modified during the execution of the program. You can avoid parsing the file up the point where it is needed knowing that performing the read in the beginning or at a later time will yield the same result.
In any case - make note that this is no longer going to be thread safe. You can often rely on an object to be thread safe if it only has const methods (or you use only the const methods after initialization). But if those const methods are only logically const, then you lose that benefit (unless of course you start locking it all the time).
The only compiler optimization that could cause havok would be for the compiler to figure out that you're calling the method twice with the same arguments, and just reuse the first return value - which is fine as long as the function truly is logically const, and returns the same value for a given set of arguments (and object state). Even that optimization isn't valid if it's possible that anyone (including another thread) has had access to the object to call non-const methods on it.
mutable was added to the language specifically for this case. C++ is a pragmatic language, and is happy to allow corner cases like this to exist for when they are needed.