"get() const" vs. "getAsConst() const" - c++

Someone told me about a C++ style difference in their team. I have my own viewpoint on the subject, but I would be interested by pros and cons coming from everyone.
So, in case you have a class property you want to expose via two getters, one read/write, and the other, readonly (i.e. there is no set method). There are at least two ways of doing it:
class T ;
class MethodA
{
public :
const T & get() const ;
T & get() ;
// etc.
} ;
class MethodB
{
public :
const T & getAsConst() const ;
T & get() ;
// etc.
} ;
What would be the pros and the cons of each method?
I am interested more by C++ technical/semantic reasons, but style reasons are welcome, too.
Note that MethodB has one major technical drawback (hint: in generic code).

C++ should be perfectly capable to cope with method A in almost all situations. I always use it, and I never had a problem.
Method B is, in my opinion, a case of violation of OnceAndOnlyOnce. And, now you need to go figure out whether you're dealing with const reference to write the code that compiles first time.
I guess this is a stylistic thing - technically they both works, but MethodA makes the compiler to work a bit harder. To me, it's a good thing.

Well, for one thing, getAsConst must be called when the 'this' pointer is const -- not when you want to receive a const object. So, alongside any other issues, it's subtly misnamed. (You can still call it when 'this' is non-const, but that's neither here nor there.)
Ignoring that, getAsConst earns you nothing, and puts an undue burden on the developer using the interface. Instead of just calling "get" and knowing he's getting what he needs, now he has to ascertain whether or not he's currently using a const variable, and if the new object he's grabbing needs to be const. And later, if both objects become non-const due to some refactoring, he's got to switch out his call.

Personally, I prefer the first method, because it makes for a more consistent interface. Also, to me getAsConst() sounds just about as silly as getAsInt().
On a different note, you really should think twice before returning a non-const reference or a non-const pointer to a data member of your class. This is an invitation for people to exploit the inner workings of your class, which ideally should be hidden. In other words it breaks encapsulation. I would use a get() const and a set(), and return a non-const reference only if there is no other way, or when it really makes sense, such as to give read/write access to an element of an array or a matrix.

Given the style precedent set by the standard library (ie begin() and begin() const to name just one example), it should be obvious that method A is the correct choice. I question the person's sanity that chooses method B.

So, the first style is generally preferable.
We do use a variation of the second style quite a bit in the codebase I'm currently working on though, because we want a big distinction between const and non-const usage.
In my specific example, we have getTessellation and getMutableTessellation. It's implemented with a copy-on-write pointer. For performance reasons we want the const version to be use wherever possible, so we make the name shorter, and we make it a different name so people don't accidentally cause a copy when they weren't going to write anyway.

While it appears your question only addresses one method, I'd be happy to give my input on style. Personally, for style reasons, I prefer the former. Most IDEs will pop up the type signature of functions for you.

I would prefer the first. It looks better in code when two things that essentially do the same thing look the same. Also, it is rare for you to have a non-const object but want to call the const method, so that isn't much of a consern (and in the worst case, you'd only need a const_cast<>).

The first allows changes to the variable type (whether it is const or not) without further modification of the code. Of course, this means that there is no notification to the developer that this might have changed from the intended path. So it's really whether you value being able to quickly refactor, or having the extra safety net.

The second one is something related to Hungarian notation which I personally DON'T like so I will stick with the first method.
I don't like Hungarian notation because it adds redundancy which I usually detest in programming. It is just my opinion.

Since you hide the names of the classes, this food for thought on style may or may not apply:
Does it make sense to tell these two objects, MethodA and MethodB, to "get" or "getAsConst"? Would you send "get" or "getAsConst" as messages to either object?
The way I see it, as the sender of the message / invoker of the method, you are the one getting the value; so in response to this "get" message, you are sending some message to MethodA / MethodB, the result of which is the value you need to get.
Example: If the caller of MethodA is, say, a service in SOA, and MethodA is a repository, then inside the service's get_A(), call MethodA.find_A_by_criteria(...).

The major technological drawback of MethodB I saw is that when applying generic code to it, we must double the code to handle both the const and the non-const version. For example:
Let's say T is an order-able object (ie, we can compare to objects of type T with operator <), and let's say we want to find the max between two MethodA (resp. two MethodB).
For MethodA, all we need to code is:
template <typename T>
T & getMax(T & p_oLeft, T & p_oRight)
{
if(p_oLeft.get() > p_oRight.get())
{
return p_oLeft ;
}
else
{
return p_oRight ;
}
}
This code will work both with const objects and non-const objects of type T:
// Ok
const MethodA oA_C0(), oA_C1() ;
const MethodA & oA_CResult = getMax(oA_C0, oA_C1) ;
// Ok again
MethodA oA_0(), oA_1() ;
MethodA & oA_Result = getMax(oA_0, oA_1) ;
The problem comes when we want to apply this easy code to something following the MethodB convention:
// NOT Ok
const MethodB oB_C0(), oB_C1() ;
const MethodB & oB_CResult = getMax(oB_C0, oB_C1) ; // Won't compile
// Ok
MethodA oB_0(), oB_1() ;
MethodA & oB_Result = getMax(oB_0, oB_1) ;
For the MethodB to work on both const and non-const version, we must both use the already defined getMax, but add to it the following version of getMax:
template <typename T>
const T & getMax(const T & p_oLeft, const T & p_oRight)
{
if(p_oLeft.getAsConst() > p_oRight.getAsConst())
{
return p_oLeft ;
}
else
{
return p_oRight ;
}
}
Conclusion, by not trusting the compiler on const-ness use, we burden ourselves with the creation of two generic functions when one should have been enough.
Of course, with enough paranoia, the secondth template function should have been called getMaxAsConst... And thus, the problem would propagate itself through all the code...
:-p

Related

C++ const accessors and references best practice

In attempting to brush up on my C++, I've been trying to find out the best-practice way of creating accessors.
I want to clarify my understanding and find out if what I'm doing is right. I have several questions, but they seem pretty simple so I've rolled them all into this one Stack Overflow question.
The following is some example code representing what I 'think' is the correct way of doing things:
class MyClass
{
private:
std::string StringMember_;
int IntMember_;
public:
MyClass(const std::string &stringInput, const int &intInput) : StringMember_(stringInput), IntMember_(intInput)
{
}
const std::string &StringMember() const
{
return StringMember_;
}
void StringMember(const std::string &stringInput)
{
StringMember_ = stringInput;
}
const int &IntMember() const
{
return IntMember_;
}
void IntMember(const int &intInput)
{
IntMember_ = intInput;
}
};
My questions are:
Where my accessors return a const reference variable, ie const std::string, this means that it (my class's member variable) cannot be changed. Is that correct?
The last const before a method's body indicates that no members of the class for which that method is a part of can be altered, unless they are designated mutable. Is this also correct?
Where I'm passing in const method parameters, this means that I ensure these parameters are always stored as they were passed in, thus protecting any original variables being passed in from being altered by the method body. Is this also correct?
With regards to the mutable keyword, under what circumstances would I actually want to use this? I've been struggling to think of a good scenario where I'd have a const method that needed to modify class members.
Accessors seem like a good idea, even for data that will never be publicly exposed, because it ensures a single-point of entry, allowing for easier debugging and so on. Am I thinking along the right lines here, or is this in fact totally meaningless, and that there is no need for private accessors?
From a purely syntactical perspective, should I be writing my references like const int& intInput or const int &intInput. Does it really matter where the ampersand is, or is it just a matter of personal preference?
Finally, is what I'm doing in the example above good practice? I plan to start working on a larger personal project, and I want to have these core basics down before I start running into problems later.
I was using this as a reference: https://isocpp.org/wiki/faq/const-correctness
Where my accessors return a const reference variable, ie const std::string, this means that it (my class's member variable) cannot be changed. Is that correct?
Correct. A variable cannot be changed through a const reference.
The last const before a method's body indicates that no members of the class for which that method is a part of can be altered, unless they are designated mutable. Is this also correct?
Correct. It also allows the function to be called on a const object.
Where I'm passing in const method parameters, this means that I ensure these parameters are always stored as they were passed in, thus protecting any original variables being passed in from being altered by the method body. Is this also correct?
Correct. Same can be achieved with accepting the argument by value.
With regards to the mutable keyword, under what circumstances would I actually want to use this?
See When have you used C++ 'mutable' keyword?
Accessors seem like a good idea, even for data that will never be publicly exposed, because it ensures a single-point of entry, allowing for easier debugging and so on. Am I thinking along the right lines here
I don't buy this argument. Watchpoints allow for easy debugging of member variables regardless of where they're accessed from.
From a purely syntactical perspective, should I be writing my references like const int& intInput or const int &intInput.
Both are syntactically equivalent and the choice between them is purely aesthetic.
Finally, is what I'm doing in the example above good practice?
There is no general answer. Accessors are sometimes useful. Often they're redundant. If you provide a function that allows setting the value directly, such as you do here, then you might as well get rid of the accessors and make the member public.
Seems to me like you have a pretty good handle on the concepts here. As far as a mutable example there are lots, here's one: you have a search method, and for performance reasons you cache the last search results... that internal cache would need to be mutable for a const search method. I.e. the external behavior didn't change, but internally something might change.
Here is some examples for mutable:
memoiziation caches, for when something is referencially-transparent,
but expensive to calculate, the first call to the (const-qualified)
accessor calculates the value and stores it in a mutable member hash
table, second and subsequent calls fetch the value from the table
instead.
access counters, timing, loggers, and other instrumentation that needs
to change some state when a const-qualified accessor is called
From https://www.quora.com/When-should-I-actually-use-a-mutable-keyword-in-C++

Can I use const references instead of getter functions?

I just wondered if I could bypass using getters if I just allowed a const reference variable, as follows
#include <string>
class cTest
{
private:
int m_i;
std::string m_str;
public:
const int & i;
const std::string & str;
cTest(void)
: i(m_i)
, str(m_str)
{}
};
int main(int argc, char *argv[])
{
cTest o;
int i = o.i; // works
o.i += 5; // fails
o.str.clear(); // fails
return 0;
}
I wonder why people do not seem to do this at all. Is there some severe disadvantage I am missing? Please contribute to the list of advantages and disadvantages, and correct them if necessary.
Advantages:
There is no overhead through calls of getter functions.
The program size is decreased because there are less functions.
I can still modify the internals of the class, the reference variables provide a layer of abstraction.
Disadvantages:
Instead of getter functions, I have a bunch of references. This increases the object size.
Using const_cast, people can mess up private members, but these people are mischievous, right?
Some severe disadvantages indeed (aside from the 2nd disadvantage that you also mention which I also put in the "severe" category):
1) You'll need to supply (and therefore maintain) a copy constructor: the compiler default will not work.
2) You'll need to supply an assignment operator: the compiler default will not work.
3) Think carefully about implementing the move semantics. Again, the compiler default will not work.
These three things mean the const reference anti-pattern you propose is a non-starter. Don't do it!
One advantage of getter functions is that you might at some point in time - want to alter returned value - and without getter function you cannot do it. This scenerio would require you to return non reference actually, which is less common in c++. [edit] but with move semantics instead of references this should be doable[/edit]
You might also want to put a breakpoint into getter function to learn who is reading its value, you might want to add logging, etc. This is called
encapsulation.
Other advantage of getter is that in debug builds you can add additional checks/asserts on returned data.
In the end compiler will inline your getter functions, which will result in similar code to the one you propose.
some additional disadvantage:
1) template code will want to get values using function call, ie. size(), if you change it to const& variable then you will not be able to use it in some templates. So this is a consistency problem.
If you want to avoid getters and setters, using a const reference member is not the solution.
Instead, you'll want to ensure const correctness on the surrounding struct (which automagically gives you const access to the members), and just let the members be whatever they logically need to be.
Be sure to read up on when getters and setters can, should, or could be switched with public data members. See e.g. this question. Just note that if you change the interface, the heralded dvantage of setters/getters is that calling the getter won't affect call sites. Reality seems to argue otherwise, and e.g. refactoring a member along with all its access points is a trivial operation for any self-respecting C++ code editor.
Although one could argue for encapsulation, I'd more strongly argue for const correctness, which alleviates the need for much encapsulation and really simplifies code quite a lot.

C++ Getter/Setter (Alternatives?)

Okay, just about everywhere I read, I read that getters/setters are "evil".
Now, as a programmer who uses getters/setters often in PHP / C#, I do not see how they are alive. I have read that they break encapsulation, etc etc, however, here is a simple example.
class Armor{
int armorValue;
public:
Armor();
Armor(int); //int here represents armor value
int GetArmorValue();
void SetArmorValue(int);
};
Now, lets say getters and setters are "evil".
How are you supposed to change a member variable after initialization.
Example:
Armor arm=Armor(128); //armor with 128 armor value
//for some reason I would like to change this armor value
arm.SetArmorValue(55); //if i do not use getters / setters how is this possible?
Lets say the above is not okay, for whatever reason.
What if my game restricts armor values from 1 to 500. (No armor can have a piece that has more than 500 armor or less than 1 armor).
Now my implementation becomes
void Armor::SetArmor(int tArmValue){
if (tArmValue>=1 && tArmValue<=500)
armorValue=tArmValue;
else
armorValue=1;
}
So, how else would I impose this restriction without using getters/setters?
How else would I modify a property without using getters/setters?
Should armorValue just be a public member variable in case 1, and the getters/setters used in case 2?
Curious. THanks guys
You have misunderstood something. Not using getters/setters breaks encapsulation and exposes implementation details, and can be considered "evil" for some definition of evil.
I guess they can be considered evil in the sense, that without proper IDE/editor support, they are somewhat tediois to write in C++...
One pitfall of C++ is to create non-const reference getter, which allows also modification. That's same as returning a pointer to internal data, and will lock that part of internal implementation, and is really no better than making field public.
Edit: based on comments and other answers, what you heard probably refers to always creating non-private getter and setter for every field. But I would not call that evil either, just stupid ;-)
Being slightly contrarian: yes, getters and setters (aka accessors and mutators) are mostly evil.
The evil here is not, IMO, so much from "breaking encapsulation", as from simply defining a variable to be of one type (e.g., int) when it's really not that type at all. Looking at your example, you're calling Armor an int, but it's really not. While it's undoubtedly an integer, it's certainly not an int, which (among other things) defines a range. While your type is an integer, it's never intended to support the same range as an int at all. If you want Armor to be of a type integer from 1 to 500, define a type to represent that directly, and define Armor as an instance of that type. In this case, since the invariant you want to enforce is defined as part of the type itself, you don't need to tack a setter onto it to try to enforce it.
template <class T, class less=std::less<T> >
class bounded {
const T lower_, upper_;
T val_;
bool check(T const &value) {
return less()(value, lower_) || less()(upper_, value);
}
void assign(T const &value) {
if (check(value))
throw std::domain_error("Out of Range");
val_ = value;
}
public:
bounded(T const &lower, T const &upper)
: lower_(lower), upper_(upper) {}
bounded(bounded const &init)
: lower_(init.lower), upper_(init.upper), val_(init.val_)
{ }
bounded &operator=(T const &v) { assign(v); return *this; }
operator T() const { return val_; }
friend std::istream &operator>>(std::istream &is, bounded &b) {
T temp;
is >> temp;
if (b.check(temp))
is.setstate(std::ios::failbit);
else
b.val_ = temp;
return is;
}
};
With this in place, defining some armor with a range of 1..500 becomes utterly trivial:
bounded<int> armor(1, 500);
Depending on the situation, you might prefer to define (for example) a saturating type where attempting to assign an out of range value is fine, but the value that actually is assigned will simply be the nearest value that is within range.
saturating<int> armor(1, 500);
armor = 1000;
std::cout << armor; // prints "500"
Of course, what I've given above is also a bit bare-bones. For your armor type, it would probably be convenient to support -= (and possibly +=) so an attack would end up something like x.armor -= 10;.
Bottom line: the (or at least "one") major problem with getters and setters is that they usually point to your having defined a variable as being of one type when you really want some other type that happened to be sort of similar in a few ways.
Now, it's true that some languages (e.g., Java) fail to provide the programmer with the tools necessary to write code like that. Here I'm trusting your use of the C++ tag to indicate that you really do want to write C++ though. C++ does provide you with the necessary tools, and (at least IMO) your code will be better off for your making good use of the tools it provides so your type enforces the required semantic constraints while still using clean, natural, readable syntax.
In short: they aren't evil.
It's nothing wrong with them as long as they don't leak out the internal representation. I see no problems here.
A common criticism of get/set functions is that they can be abused by client code to perform operations that logically should be encapsulated in the class. For example, say a client wants to "polish" their armour, and decides the effect is to increase "value" by 20, so they do their little get and set thing and are happy. Then someone other client code elsewhere decides rusty armour should drop the value by 30, and they do their bit. Meanwhile, a dozen other places in client code are also allowing polishing and rusting effects on armour - as well as say "reinforcing" and "cracking", and implementing them directly. There's no central control of this... the maintainer of the armour class has no ability to do things like:
have the rust, polish, reinforce and crack effects apply at most once per piece of armour
tune the number added to or subtract from value for specific logical effects
decide that the new "leather" armour type can't rust, and ignore client attempts to make it do so
On the other hand, if the first client that wanted to make armour rusty couldn't do so through the interface, they'd go to the maintainer of the armour class and say "hey, give me a function to do this", then other people could start using the logical-level "rust" operation, and if it became useful later to do the kinds of things I describe above they could be implemented easily and centrally in the armour class (e.g. by having a separate boolean to say if the armour was rusty, or a separate variable recording the rust effect).
So, the thing with get/set functions is they frustrate the natural evolution of an API of logical functionality, instead distributing logic throughout client code, leading in extremis to an unmaintainable mess.
Your getter/setter looks ok.
The alternative to getter/setters is to make member variables public. To be more precise, group variables into structure without member functions. And operate on this structure within your class
Giving access to members reduces encapsulation, but sometimes it's necessary. And the best way to do it is by means of getters and setters. Some people implement them when no such access is necessary, just because they can and it's a habit.
Getters are evil whenever:
They access directly data members of the class
When you have to add new getter every time you add data to the class
The data behaviour is different in each getter
Good getters would thus do the following:
They forward the request to some other object or collect the data from several places
You can fetch large amounts of data using just one getter
All the data you fetch is handled the same way
Setters on the other hand are evil always.
how else would I impose this restriction without using getters/setters? How else would I modify a property without using getters/setters?
You can check what you read from the variable and if its value is out of range use a predefined value instead (if possible).
You can also resort to dirty hacks such as protecting the memory underneath the variable from writing, catching write attempts and disallowing/ignoring the ones with invalid values. This is going to be cumbersome to implement and expensive to execute. It may be useful for debugging, though.

Does it make sense to use const in an interface or not?

I have a module that performs some calculations and during the calculations, communicates with other modules. Since the calculation module does not want to rely on the other modules, it exposes an interface like this (this is a very simplified version of course):
class ICalculationManager
{
public:
double getValue (size_t index) = 0;
void setValue (size_t index, double value) = 0;
void notify (const char *message) = 0;
};
Applications that want to use the calculation module need to write their own implementation of the interface, and feed it to the calculation tool, like this:
MyCalculationManager calcMgr;
CalculationTool calcTool (calcMgr);
calcTool.calculate();
I am wondering now whether it makes sense to add "const" to the methods of the ICalculationManager interface.
It would seem logical that the getValue method only gets something and doesn't change anything, so I could make this const. And setValue probably changes data so that won't be const.
But for a more general method like notify I can't be sure.
In fact, for none of the methods I can now for sure that the method is really implemented as a const method, and if I would make the interface methods const, I am forcing all implementations to be const as well, which is possibly not wanted.
It seems to me that const methods only make sense if you know beforehand what your implementation will be and whether it will be const or not. Is this true?
Doesn't it make sense to make methods of this kind of interface const? And if it makes sense, what are good rules to determine whether the method should be const or not, even if I don't know what the implementation will be?
EDIT: changed the parameter from notify from "char *" to "const char *" since this lead to irrelevant answers.
You make a function const when you are advertising to clients that calling the function will never change the externally visible state of the object. Your object only has one piece of state that can be retrieved, getValue.
So, if getValue can cause the next getValue to return a different value then sure, leave it non-const. If you want to tell clients that calling getValue() will never change the value returned by the next getValue() then make it const.
Same for notify:
double d1 = mgr->getValue(i);
mgr->notify("SNTH"); // I'm cheating.
double d2 = mgr->getValue(i);
assert(d1==d2);
If that should hold true for all cases and all i's then notify() should be const. Otherwise it should not be.
Yes. One should use const whenever and wherever it is sensible to do so. It doesn't make sense that the method for performing a calculation (which is what your interface suggests) should change it's observable behavior because it had "notify" called on it. (And for that matter, how is notification related to calculation at all?)
My making one of the interface members const, you don't force clients to be const -- you merely allow them use of a const ICalculationManager.
I would probably make Notify const. If clients need to do something non-const as a result of a notification, then Notify is not a good method name -- that name suggest non-state-modifying transformations such as logging, not modification.
For instance, most of the time you pass your interface around, you're going to want to use pass-by-reference-to-const to pass the interface implementor, but if the methods aren't const, you cannot do that.
The interface should be guiding the implementation, not the other way around. If you haven't decided if a method or parameter can be const or not, you're not done designing.
Using const is a way of making assertions about what the code is or is not allowed to do. This is extremely valuable in reasoning about a piece of code. If your parameter to notify isn't const for example, what changes would it make to the message? How would it make the message larger if it needed to?
Edit: You appear to know the value of declaring a const parameter, so lets build on that. Suppose you want a function to record the value of a calculation:
void RecordCalculation(const ICalculationManager *calculation);
The only methods you'll be able to call on that pointer are the const methods. You can be sure that after the function returns, the object will be unchanged. This is what I meant by reasoning about the code - you can be absolutely certain the object won't be changed, because the compiler will generate an error if you try.
Edit 2: If your object contains some internal state that will be modified in response to operations that are logically const, such as a cache or buffer, go ahead and use the mutable keyword on those members. That's what it was invented for.
For me it only depends on the contract of your interface.
For a getter method I do not see why it should change any data and if this happens, maybe mutable is an option.
For the setter method I agree, not const there because this will certainly change data somehow.
For the notify is hard to say without knowing what it means for your system. Also, do you expect the message parameter to be modified by the implementation? If now, it should be const too.
Without reading your entire post: Yes of course, it makes sense if you want to use an object (which inherits ICalculationManager) in a const context. Generally, you should always use const qualifier if you don't manipulate private data.
EDIT:
Like Mark Ransom said: You need to know exactly how your interface functions should behave, otherwise your not finished designing.
I know I'm going to get a lot of downvotes for this, but in my opinion the usefulness of const-correctness in C++ is vastly exaggerated. The const idea is primitive (it only captures one bit of concept... change/don't change) and comes with an high cost that even includes necessity of code duplication. Also it doesn't scale well (consider const_iterators).
What's more important I cannot remember even a single case (not even ONE) in which the const-correctness machinery helped me by spotting a true logical error, that is I was trying to do something that I shouldn't do. Instead every single time the compiler stopped me there was a problem in the const declaration part (i.e. what I was trying to do was logically legit, but a method or a parameter had a problem in the declaration about const-ness).
In all cases I can remember where I got a compiler error related to const-correctness the fix was just adding some missing const keywords or removing some that were in excess... without using the const-correctness idea those errors wouldn't have been there at all.
I like C++, but of course I don't love to death every bit of it (digression: when I interview someone a question I often ask is "what is the part you don't like about <language> ?" ... if the answer is "none" then simply means that who I'm talking to is still in the fanboy stage and clearly doesn't have a big real experience).
There are many parts of C++ that are very good, parts that are IMO horrible (stream formatting, for example) and parts that are not horrible but neither logically beautiful nor practically useful. Const-correctness idea is IMO in this gray area (and this is not a newbie impression... I came to this conclusion after many many lines and years of coding in C++).
May be it's me, but apparently const correctness solves a problem that my brain doesn't have ... I've many others problems, but not the one of confusing when I should change an instance state and when I shouldn't.
Unfortunately (differently from stream formatting) you cannot just ignore the const-correctness machinery in C++ because it's part of the core language, so even if I don't like it I'm forced to comply with it anyway.
You may now say... ok, but what's the answer to the question ? It's simply that I wouldn't get too crazy about that part of the semantic description... it's just a single bit and comes with an high price; if you're unsure and you can get away without declaring constness then don't do it. Constness of references or methods is never an help for the compiler (remember that it can be legally casted away) and it has been added to C++ just as an help for programmers. My experience tells me however that (given the high cost and the low return) it's not a real help at all.

Sell me const-correctness

So why exactly is it that it's always recommended to use const as often as possible? It seems to me that using const can be more of a pain than a help in C++. But then again, I'm coming at this from the python perspective: if you don't want something to be changed, don't change it. So with that said, here are a few questions:
It seems like every time I mark something as const, I get an error and have to change some other function somewhere to be const too. Then this causes me to have to change another function somewhere else. Is this something that just gets easier with experience?
Are the benefits of using const really enough to compensate for the trouble? If you don't intend to change an object, why not just not write code that changes it?
I should note that at this point in time, I'm most focused on the benefits of using const for correctness and maintainability purposes, although it is also nice to have an idea of the performance implications.
This is the definitive article on "const correctness": https://isocpp.org/wiki/faq/const-correctness.
In a nutshell, using const is good practice because...
It protects you from accidentally changing variables that aren't intended be changed,
It protects you from making accidental variable assignments. For instance, you are protected from
if( x = y ) // whoops, meant if( x == y ).
The compiler can optimize it.
At the same time, the compiler can generate more efficient code because it knows exactly what the state of the variable/function will be at all times. If you are writing tight C++ code, this is good.
You are correct in that it can be difficult to use const-correctness consistently, but the end code is more concise and safer to program with. When you do a lot of C++ development, the benefits of this quickly manifest.
Here's a piece of code with a common error that const correctness can protect you against:
void foo(const int DEFCON)
{
if (DEFCON = 1) //< FLAGGED AS COMPILER ERROR! WORLD SAVED!
{
fire_missiles();
}
}
It seems like every time I mark
something as const, I get an error and
have to change some other function
somewhere to be const too. Then this
causes me to have to change another
function somewhere else. Is this
something that just gets easier with
experience?
From experience, this is a total myth. It happens when non const-correct sits with const-correct code, sure. If you design const-correct from the start, this should NEVER be an issue. If you make something const, and then something else doesn't complile, the compiler is telling you something extremely important, and you should take the time to fix it properly.
If you use const rigorously, you'd be surprised how few real variables there are in most functions. Often no more than a loop counter. If your code is reaching that point, you get a warm feeling inside...validation by compilation...the realm of functional programming is nearby...you can almost touch it now...
It's not for you when you are writing the code initially. It's for someone else (or you a few months later) who is looking at the method declaration inside the class or interface to see what it does. Not modifying an object is a significant piece of information to glean from that.
Programming C++ without const is like driving without the safety belt on.
It's a pain to put the safety belt on each time you step in the car, and 364 out of 365 days you'll arrive safely.
The only difference is that when you get in trouble with your car you'll feel it immediately, whereas with programming without const you may have to search for two weeks what caused that crash only to find out that you inadvertently messed up a function argument that you passed by non-const reference for efficiency.
const is a promise your are making as a developer, and enlisting the compiler's help in enforcing.
My reasons for being const-correct:
It communicates to clients of your function that your will not change the variable or object
Accepting arguments by const reference gives you the efficiency of passing by reference with the safety of passing by value.
Writing your interfaces as const correct will enable clients to use them. If you write your interface to take in non-const references, clients who are using const will need to cast constness away in order to work with you. This is especially annoying if your interface accepts non-const char*'s, and your clients are using std::strings, since you can only get a const char* from them.
Using const will enlist the compiler in keeping you honest so you don't mistakenly change something that shouldn't change.
My philosophy is that if you're going to use a nit-picky language with compile time checks than make the best use of it you can. const is a compiler enforced way of communicating what you mean... it's better than comments or doxygen will ever be. You're paying the price, why not derive the value?
For embedded programming, using const judiciously when declaring global data structures can save a lot of RAM by causing the constant data to be located in ROM or flash without copying to RAM at boot time.
In everyday programming, using const carefully helps you avoid writing programs that crash or behave unpredictably because they attempt to modify string literals and other constant global data.
When working with other programmers on large projects, using const properly helps prevent the other programmers from throttling you.
const correctness is one of those things that really needs to be in place from the beginning. As you've found, its a big pain to add it on later, especially when there is a lot of dependency between the new functions you are adding and old non-const-correct functions that already exist.
In a lot of the code that I write, its really been worth the effort because we tend to use composition a lot:
class A { ... }
class B { A m_a; const A& getA() const { return m_a; } };
If we did not have const-correctness, then you would have to resort to returning complex objects by value in order to assure yourself that nobody was manipulating class B's internal state behind your back.
In short, const-correctness is a defensive programming mechanism to save yourself from pain down the road.
const helps you isolate code that "change things" behind your back. So, in a class, you'd mark all methods that don't change the state of the object as const. This means that const instances of that class will no longer be able to call any non-const methods. This way, you're prevented from accidentally calling functionality that can change your object.
Also, const is part of the overload mechanism, so you can have two methods with identical signatures, but one with const and one without. The one with const is called for const references, and the other one is called for non-const references.
Example:
#include <iostream>
class HelloWorld {
bool hw_called;
public:
HelloWorld() : hw_called(false) {}
void hw() const {
std::cout << "Hello, world! (const)\n";
// hw_called = true; <-- not allowed
}
void hw() {
std::cout << "Hello, world! (non-const)\n";
hw_called = true;
}
};
int
main()
{
HelloWorld hw;
HelloWorld* phw1(&hw);
HelloWorld const* phw2(&hw);
hw.hw(); // calls non-const version
phw1->hw(); // calls non-const version
phw2->hw(); // calls const version
return 0;
}
Say you have a variable in Python. You know you aren't supposed to modify it. What if you accidentally do?
C++ gives you a way to protect yourself from accidentally doing something you weren't supposed to be able to do in the first place. Technically you can get around it anyways, but you have to put in extra work to shoot yourself.
There is a nice article here about const in c++. Its a pretty straight forward opinion but hope it helps some.
When you use the "const" keyword, you're specifying another interface to your classes. There is an interface that includes all methods, and an interface that includes only the const methods. Obviously this lets you restrict access to some things that you don't want changed.
Yes, it does get easier with time.
I like const correctness ... in theory. By every time I have tried to apply it rigourously in practice it has broken down eventually and const_cast starts to creep in making the code ugly.
Maybe it is just the design patterns I use, but const always ends up being too broad a brush.
For example, imagine a simple database engine ... it has schema objects, tables, fields etc. A user may have a 'const Table' pointer meaning that they are not allowed to modify the table schema itself ... but what about manipulating the data associated with the table? If the Insert() method is marked const then internally it has to cast the const-ness away to actually manipulate the database. If it isn't marked const then it doesn't protect against calling the AddField method.
Maybe the answer is to split the class up based on the const-ness requirements, but that tends to complicate the design more than I would like for the benefit it brings.
You can give the compiler hints with const as well....as per the following code
#include <string>
void f(const std::string& s)
{
}
void x( std::string& x)
{
}
void main()
{
f("blah");
x("blah"); // won't compile...
}