What to do when library function parameters aren't const - c++

Most of my code-base is immutable; however, due to quirks of the language design, I'm unable to mark my variables const.
In a vast majority of cases, especially when inter-operating with C code, I find function parameters not marked const, even though they provably do not modify them.
One such example is fts_open(...). At this point the compiler forces me to tediously remove const qualifiers from large parts of my code, and thereby removing the safety it offered.
One trivial solution is to compile with -fpermissive, but this is completely contrary to my intent.
Apart from rewriting every single C library ever written, what can I do to still get the benefits from leaning on the compiler?
i.e. this type of code does not work:
void function(immutable_type const &param)
{
char const * const fts_arg[2]{std::data(param.path), nullptr};
FTS *tree = fts_open(fts_arg, FTS_OPTIONS, nullptr);
...
}
At this point I have to:
Remove const from the fts_args variable.
Remove const from the function parameter.
Remove const from path inside the datatype definition.
Remove const from the variable being passed to function.
Recursively remove consts from the entire call chain.
Thank you. :)

This is exactly what const_cast is for. If you absolutely know that a function won't change the pointed/referenced object, then it is OK to const_cast the constness of your pointer/reference away in order to pass it into that function, despite referring to a const object.
isn't const_casting from const undefined behavior?
No. const_cast itself is never UB. But modifying a const object is. So if you cannot prove that a function taking a non-const pointer/reference doesn't modify the object, then it is not safe to pass the const_casted reference into that function.
Also consider whether the implementation might be changed in future to use non-constness.
In case where you cannot prove that the non-constly referred/pointed object won't be modified, you can make a local copy of the constly referred argument of your wrapper function. The overhead of this copy may be trivial (int) or non-trivial (long std::vector).
If you cannot prove that the object won't be modified, and copying is expensive (or not possible), then as last resort, you have to get rid of constness of your own argument (and propagate the change up the call chain). Or use another API in the implementation.

Related

Is there any downside of declaring function parameter as const pointer to const

A lot of books and experts say when talking about declaring functions/methods that you should put const everywhere you can unless you know that you need to modify that parameter. But I haven't seen much const pointer to const in the codes. Though if I have a pointer, and function dosn't need to modify neither pointer itself, nor pointed value, it seems to be a good choice to write this parameter as a const pointer to const. But mostly I've seen in such cases pointer to const is used. Is there any downside of declaring parameter as const pointer to const comparing with just pointer to const?
There is no downside and it has the upside that the compiler will warm you with an error if you accidentally attempt to overwrite the value of the pointer (just like with declaring non-pointer parameters as const).
I suspect the reason why people don't use const pointer to const more often is that it subjectively looks a little clunky but that's no reason not to use it in your own code. And another reason is that using const everywhere is not always as common as using it sparingly.
Personally I tend to use const everywhere when possible except in function signatures when it doesn't change the external interface (the caller of the function doesn't care if it's const int or just int) only for the sake of keeping the function signature concise, but it's not uncommon to also use const in function parameters in general.
Top level const in function signatures is nearly meaningless. You can add/remove it at will. (A reference to const is not top level)
Elsewhere, yes a const pointer to const data is reasonable.
Is there any downside of declaring parameter as const pointer to const comparing with just pointer to const?
Same downsides apply as passing any parameter by const value. Perhaps some downsides apply to pointers slightly stronger:
Obviously, it prevents the parameter from being modified. Typically, pointers are used as iterators, which are generally used for iteration which requires modification of the iterator. If you don't need the pointer for iteration, then are you sure you need a pointer in the first place? ... You might, but those cases are quite rare.
It is unnecessarily longer and more complex to write and to read. This is especially noticeable in case of compound types such as pointers which inherently impose additional cognitive load. The constness is irrelevant information to the caller. Showing only relevant information, and keeping meaningless boilerplate minimal is essential to keep the source readable.
You cannot overload "top-level" const parameter with a non-const parameter and vice versa. Both declare the same function! Restraining oneself to only use non-const parameters reduces the risk of being confused by the restriction on overloads.
It isn't conventional. As you wrote: But mostly I've seen .... Conventionality is a very weak argument, but unless you can provide a better argument against it, it is still an argument.
Sure, these aren't the strongest of downsides and somewhat subjective... but then again, so are the upsides. So in the end, it doesn't matter much one way or the other. Perhaps a middle ground is to declare the parameter non-const in header but decare it const in the definition.

How do I know when to use `const ref` or `in`?

void foo(T, size_t size)(in T[size] data){...}
//vs
void foo(T, size_t size)(const ref T[size] data){...}
According to https://stackoverflow.com/a/271344/944430 it seems that in C++ pass by value can be faster in some situations.
But D has a special keyword in and I am wondering when I should use it. Does in always result in a copy or is it a compiler optimization?
Are there any guidelines that I can follow that help me decide between const ref and in?
I would argue that you should never use in on function parameters. in is an artifact from D1 that was kept to reduced code breakage but was changed to be equivalent to const scope. So, every time you think of typing in on a function parameter, think of const scope, since that's what you're really doing. And scope currently only does anything with delegates, in which case, it's telling the compiler that the function taking the delegate is not going to return it or assign it to anything and that therefore no closure has to be allocated to hold the state of that delegate (so, it improves efficiency in many cases for delegates), whereas for all other types, it's completely ignored, which means that using it is meaningless (and potentially confusing), and if it ever does come to mean something else for other types (e.g. it's been suggested that it should enforce that a pointer that's passed in as scope can't escape the function), then the semantics of your code could change in unexpected ways. Presumably, it'll be accompanied by the appropriate warnings when the happens, but why mark your code with a meaningless attribute that could have meaning later and thus force you to change your code? At this point, scope should only be used on delegates, so in should only be used on delegates, and you don't usually want const delegates. So, just don't use in.
So, ultimately, what you're really asking is whether you should use const or const ref.
The short answer is that you generally shouldn't use ref unless you want to mutate the argument you're passing in. I would also point out that this question is meaningless for anything but structs and maybe static arrays, because classes are already reference types, and none of the built-in types (save for static arrays) cost much of anything to copy. The longer answer is...
Move semantics are built into D, so if you have a function that takes its argument by value - e.g.
auto foo(Bar bar) { ... }
then it will move the argument if it can. If you pass it an lvalue (a value that can be on the left-hand side of an assignment), then that value is going to be copied except maybe in circumstances where the compiler is able to determine that it can optimize the copy away (e.g. when the variable is never used after that function call), but that's going to depend on the compiler and compiler flags used. So, passing a variable to a function by value will usually result in a copy. However, if you pass the function an rvalue (the values that can't go on the left-hand side of an assignment), then it will move that object rather than copying it. This is different from C++, where move semantics were not introduced until C++11, and even then, they require move constructors, whereas D uses postlbit constructors, which changes it so that moves can be done by default. A couple of previous SO questions on that:
Does D have something akin to C++0x's move semantics?
Questions about postblit and move semantics
So, yes, there are cases in D where passing by ref would avoid a copy, but in D, ref always requires an lvalue (even with const). So, if you start putting ref const(T) everywhere like you'd do const T& in C++, you're going to have a lot of functions which are really annoying to call, because every temporary will have to be assigned to a variable first to call the function. So, you should seriously consider only ever using ref for when you want to mutate a variable that's passed in and not for efficiency. Certainly, your default should be to not pass by const ref, but if you do need that extra efficiency, you have two options:
Overload the function on ref-ness so that you have an overload that takes by const ref and one that takes by ref so that the lvalues get passed to one without being copied, and the rvalues get passed to the other without needing an extraneous variable. e.g.
auto foo(const Bar bar) { foo(bar); }
auto foo(ref const(Bar) bar) { ... }
And that's a bit annoying but works well enough when you only have one parameter with ref. However, you get a combinatorial explosion of overloads as more ref parameters are added. e.g.
auto foo(const Bar bar, const Glop glop) { foo(bar, glop); }
auto foo(ref const(Bar) bar, const Glop glop) { foo(bar, glop); }
auto foo(const Bar bar, ref const(Glop) glop) { foo(bar, glop); }
auto foo(ref const(Bar) bar, ref const(Glop) glop) { ... }
So, that works to a point, but it's not particularly pleasant. And if you define the overloads like I did here, then it also has the downside that the rvalues end up being passed to a wrapper function (adding an extra function call - though one that should be quite inlinable), which means that they're now passed by ref to the main overload and if one of those parameters is passed to another function or returned, the compiler can't do a move, whereas if ref hadn't been involved, then it could have. That's one of the reasons that it's now argued that you shouldn't use const T& heavily in C++11 like you would have done in C++98.
You can get around that problem by duplicating the function body for each overload, but that obviously creates a maintenance problem as well as creating code bloat.
The alternative is to use auto ref, which basically does that for you, but the function has to be templated. e.g.
auto foo()(const auto ref Bar bar, const auto ref Glop glop) { ... }
So, now you only have one overload, but it still generates all of those overloads with the full code underneath the hood every time the template is instantiated with a different combination of ref-ness. So, your code is cleaner, but you still get more bloat, and if you need to do this with a virtual function, then you're out of luck and have to go back to the more explicit overload solution, because templated functions can't be virtual.
So, in general, trying to have your functions accept const ref for efficiency reasons just gets ugly. The fact that D has move semantics built in reduces the need for it (just like with C++11, it's now argued that passing by value is often better, thanks to move semantics and how the compiler optimizes them). And it's ugly enough to do in D in the general case that unless you actually get a performance boost that matters, it's probably not worth passing by ref just for efficiency. You should probably avoid using ref for efficiency unless you've actually measured a difference in performance that's worth the pain.
The other thing to consider - separate from ref-ness - is that D's const is a lot more restrictive than C++'s const (e.g. casting away const and mutating is undefined behavior in D, and D's const is transitive). So, slapping const all over the place can sometimes become problematic - especially in generic code. So, using it can be great for preventing accidental mutation or indicating that a function does not mutate its arguments, but don't just blithely slap it everywhere that shouldn't be mutating the variable like you would in C++. Use it where it makes sense, but be aware that you will run into cases where D's const is too restrictive to be usable, even if C++'s const would have worked.
So, in most cases, when you want your function to take a T, you should default to it taking a plain T. And then if you know that efficiency is a concern, you can consider using some form of ref (probably favoring auto ref or const auto ref if you're not dealing with a virtual function). But default to not using ref. Your life will be much more pleasant that way.

Does const-correctness give the compiler more room for optimization?

I know that it improves readability and makes the program less error-prone, but how much does it improve the performance?
And on a side note, what's the major difference between a reference and a const pointer? I would assume they're stored in the memory differently, but how so?
[Edit: OK so this question is more subtle than I thought at first.]
Declaring a pointer-to-const or reference-of-const never helps any compiler to optimize anything. (Although see the Update at the bottom of this answer.)
The const declaration only indicates how an identifier will be used within the scope of its declaration; it does not say that the underlying object can not change.
Example:
int foo(const int *p) {
int x = *p;
bar(x);
x = *p;
return x;
}
The compiler cannot assume that *p is not modified by the call to bar(), because p could be (e.g.) a pointer to a global int and bar() might modify it.
If the compiler knows enough about the caller of foo() and the contents of bar() that it can prove bar() does not modify *p, then it can also perform that proof without the const declaration.
But this is true in general. Because const only has an effect within the scope of the declaration, the compiler can already see how you are treating the pointer or reference within that scope; it already knows that you are not modifying the underlying object.
So in short, all const does in this context is prevent you from making mistakes. It does not tell the compiler anything it does not already know, and therefore it is irrelevant for optimization.
What about functions that call foo()? Like:
int x = 37;
foo(&x);
printf("%d\n", x);
Can the compiler prove that this prints 37, since foo() takes a const int *?
No. Even though foo() takes a pointer-to-const, it might cast the const-ness away and modify the int. (This is not undefined behavior.) Here again, the compiler cannot make any assumptions in general; and if it knows enough about foo() to make such an optimization, it will know that even without the const.
The only time const might allow optimizations is cases like this:
const int x = 37;
foo(&x);
printf("%d\n", x);
Here, to modify x through any mechanism whatsoever (e.g., by taking a pointer to it and casting away the const) is to invoke Undefined Behavior. So the compiler is free to assume you do not do that, and it can propagate the constant 37 into the printf(). This sort of optimization is legal for any object you declare const. (In practice, a local variable to which you never take a reference will not benefit, because the compiler can already see whether you modify it within its scope.)
To answer your "side note" question, (a) a const pointer is a pointer; and (b) a const pointer can equal NULL. You are correct that the internal representation (i.e. an address) is most likely the same.
[update]
As Christoph points out in the comments, my answer is incomplete because it does not mention restrict.
Section 6.7.3.1 (4) of the C99 standard says:
During each execution of B, let L be any lvalue that has &L based on P. If L is used to
access the value of the object X that it designates, and X is also modified (by any means),
then the following requirements apply: T shall not be const-qualified. ...
(Here B is a basic block over which P, a restrict-pointer-to-T, is in scope.)
So if a C function foo() is declared like this:
foo(const int * restrict p)
...then the compiler may assume that no modifications to *p occur during the lifetime of p -- i.e., during the execution of foo() -- because otherwise the Behavior would be Undefined.
So in principle, combining restrict with a pointer-to-const could enable both of the optimizations that are dismissed above. Do any compilers actually implement such an optimization, I wonder? (GCC 4.5.2, at least, does not.)
Note that restrict only exists in C, not C++ (not even C++0x), except as a compiler-specific extension.
Off the top of my head, I can think of two cases where proper const-qualification allows additional optimizations (in cases where whole-program analysis is unavailable):
const int foo = 42;
bar(&foo);
printf("%i", foo);
Here, the compiler knows to print 42 without having to examine the body of bar() (which might not be visible in the curent translation unit) because all modifications to foo are illegal (this is the same as Nemo's example).
However, this is also possible without marking foo as const by declaring bar() as
extern void bar(const int *restrict p);
In many cases, the programmer actually wants restrict-qualified pointers-to-const and not plain pointers-to-const as function parameters, as only the former make any guarantees about the mutability of the pointed-to objects.
As to the second part of your question: For all practical purposes, a C++ reference can be thought of as a constant pointer (not a pointer to a constant value!) with automatic indirection - it is not any 'safer' or 'faster' than a pointer, just more convenient.
There are two issues with const in C++ (as far as optimization is concerned):
const_cast
mutable
const_cast mean that even though you pass an object by const reference or const pointer, the function might cast the const-ness away and modify the object (allowed if the object is not const to begin with).
mutable mean that even though an object is const, some of its parts may be modified (caching behavior). Also, objects pointed to (instead of being owned) can be modified in const methods, even when they logically are part of the object state. And finally global variables can be modified too...
const is here to help the developer catch logical mistakes early.
const-correctness generally doesn't help performance; most compilers don't even bother to track constness beyond the frontend. Marking variables as const can help, depending on the situation.
References and pointers are stored exactly the same way in memory.
This really depends on the compiler/platform (it may help optimisation on some compiler that has not yet been written, or on some platform that you never use). The C and C++ standards say nothing about performance other than giving complexity requirements for some functions.
Pointers and references to const usually do not help optimisation, as the const qualification can legally be cast away in some situations, and it is possible that the object can be modified by a different non-const reference. On the other hand, declaring an object to be const can be helpful, as it guarantees that the object cannot be modified (even when passed to functions that the compiler does not know the definition of). This allows the compiler to store the const object in read-only memory, or cache its value in a centralised place, reducing the need for copies and checks for modifications.
Pointers and references are usually implemented in the exact same way, but again this is totally platform dependant. If you are really interested then you should look at the generated machine code for your platform and compiler in your program (if indeed you are using a compiler that generates machine code).
One thing is, if you declare a global variable const, it may be possible to put it in the read-only portion of a library or executable and thus share it among multiple processes with a read-only mmap. This can be a big memory win on Linux at least if you have a lot of data declared in global variables.
Another situation, if you declare a constant global integer, or float or enum, the compiler may be able to just put the constant inline rather than using a variable reference. That's a bit faster I believe though I'm not a compiler expert.
References are just pointers underneath, implementation-wise.
It can help performance a little bit, but only if you are accessing the object directly through its declaration. Reference parameters and such cannot be optimized, since there might be other paths to an object not originally declared const, and the compiler generally can't tell if the object you are referencing was actually declared const or not unless that's the declaration you are using.
If you are using a const declaration, the compiler will know that externally-compiled function bodies, etc. cannot modify it, so you get a benefit there. And of course things like const int's are propagated at compile time, so that's a huge win (compared to just an int).
References and pointers are stored exactly the same, they just behave differently syntactically. References are basically renamings, and so are relatively safe, whereas pointers can point to lots of different things, and are thus more powerful and error-prone.
I guess the const pointer would be architecturally identical to the reference, so the machine code and efficiency would be the same. the real difference is syntax -- references are a cleaner, easier to read syntax, and since you don't need the extra machinery provided by a pointer, a reference would be stylistically preferred.

is it good practice to add const at end of member functions - where appropriate?

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).

When should a member function have a const qualifier and when shouldn't it?

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).