Should "const" be used semantically or syntactically? - c++

class A
{
...
public:
shared_ptr<Logger> GimmeLogger () const
{
return m_logger;
}
private:
shared_ptr<Logger> m_logger;
};
In class A, should GimmeLogger be const or non-const?
It would make sense to be const because it is a simple getter that doesn't modify *this (syntactic const).
But on the other hand, it returns a non-const pointer to another object that it owns (semantically non-const).

If you make that non-const, then you cannot write this:
void f(const A & a)
{
auto v = a.GimmeLogger(); //error
}
So if you want to write this; that is, if you want to call GimmeLogger on const object, then make GimmeLogger a const member function, because you cannot invoke a non-const member function, on const object. However, you can invoke a const member function, on non-const object (as well as on const object).
Inside a const member function, every member is semantically const objects. So the type of m_logger in the function becomes const share_ptr<const m_logger>. So change the return type accordingly.

Because const is a keyword, it is checked syntactically, but it should be used semantically, that is, in your design operations that don't change the visible state of your class should be marked as const.
That is the whole idea behind the mutable keyword: adding the ability to mark a member as this does not take part of the visible state of the object so that the syntactic check matches the semantic meaning. In your particular case, because you are copying a pointer, you don't even need to use mutable there (this is one of the weak points of const-correctness actually, as returning a non-const pointer does not trigger errors while compiling, even though you are opening a door for changes in your object)
In this particular case, on the other hand, I don't see a good reason by which the object would publicize it's logger... That is, const-correctness aside, why do you need to grant access to the logger?

Yes, it should be const. The const-ness of the function has nothing to do with the const-ness of the return type.
I get your point, but I think the function remains const either way.

Generally you shouldn't return a handle to a member data when you can avoid it. Try hard to review your design and find a way around this. That said, if you must, it should be const. This allows you to call the function on const objects as well as non-const objects. See for example std::string::c_str(). You can also overload the function so you get both, like standard containers do with iterators.
When in doubt, look in the standard library for a hint.

Related

Violating const correctness: What problems should I expect realistically?

I want to use a function parser library where the member function FunctionParser::Eval() is non-const, because it writes an error code to a member variable. I would like to integrate this function parser into a codebase, where this function would get called through a const member function of a class holding that function parser, thus violating const correctness.
This const member function is part of an interface, where the constness makes semantic sense. I could change that interface, so that remains an option.
As a test, I used const_cast to execute the code violating const correctness, which did not cause any errors or failed unit tests (at least not in Debug mode).
Now my question is: What problems down the road should I expect when violating const correctness in this case? (And would that ever be worth it?)
If the object is actually defined const, then casting away const-ness to modify the object is undefined behavior.
Consider instead using the mutable keyword, which declares an exception to const-ness for a given field; a mutable field is always non-const even when accessed on an object that is considered const in the current context (including from within const methods).
class foo {
private:
int a; // cannot be modified on a const foo
mutable int b; // CAN be modified on a const foo!
};
You could use the mutable keyword for fields related to error tracking.
Of course, a better pattern would be to throw an error object if an error occurs. If your API design does not permit this, making the relevant fields mutable is the correct workaround.
You're not violating const-correctness; you're merely bypassing the built-in language checks for it.
Modifying a non-const thing is fine, even if you had to cast away some constness somewhere along the way (indeed, this is what const_cast is for).
This is generally considered to be a dangerous thing to do, though, and dangerous code to write, so you should modify your interface or design. (This sounds like a good use case for mutable.)
But what runtime errors or weirdness can you expect right now? None at all.
What problems down the road should I expect when violating const correctness in this case? (And would that ever be worth it?)
If the object in question was constructed as a const object, then you definitely run into undefined behavior. However, if the object was constructed as a non-const object, then using const_cast to call a non-const member function is OK.
Simple example:
struct Foo {int f; };
void testFoo(Foo const& foo)
{
const_cast<Foo&>(foo).f = 10; // Analogous to calling a non-const member function.
}
int main()
{
Foo obj1 = {20};
Foo const obj2 = {30};
testFoo(obj1); // ok. obj1 was constructed as a non-const object.
testFoo(obj2); // Not ok. obj2 was constructed as a non-const object.
}

const correctness when don't be afraid of don't using it

Sometimes I need to pass the reference of an object. I know the benefits of const correctness in order to avoid modifications in an object and to avoid a mess, but sometimes it's really hard to use it. If I pass an object to a method that is const correct and I call another method that doesn't accept any argument and don't modify the object itself and returns nothing it complains that I need to put it as const as well. And then I'm seeing my whole application getting "const" on the end of the methods even when it returns nothing or get nothing as arguments.
void doNothing() const {}
I understand that the compiler needs to make sure that any method isn't going to modify the object but this seems to be silly.
My question is: Should I always use const correct or there's a time that I can avoid using and pass an object as reference without being concern of what can happen?
And if has a theory behind it, please explain.
For freestanding functions, you should mark reference and pointer parameters as const if the function doesn't modify the parameters. You don't need to do anything for parameters that are passed by value.
For non-static class methods, there's also an implicit this parameter. You should mark the method itself as const if it doesn't modify this. Doing so allows you to call that method on const objects. If you don't mark a method as const then you can only call it on non-const objects.
class Foo
{
int foo;
public:
void doNothing() const
{
foo = 42; // not allowed: `this` is const
}
};
The hidden this parameter is why it matters, even when a method takes no other parameters. It's the hidden parameter that you're protecting.
If something does not modify your object make it const. One simple reason to do it: You cannot call a non-const member function on a const object. That is obviously because non-const member function are a allowed to change their instance, while a const instance cannot be changed. So making a function non-const without need produces logically flawed code: Why, for example, should it be illegal to print a const Matrix? That just does not make sense, so a print function for a matrix should be const.
This applies to free functions too: Temporaries can only bind to const references, not to non-const references. So the former is just more general.
And last but not least: The only drawback you mention is writing const over and over. So I ask you: How long does it take you to type const? Surely not long enough to accept a design flaw in your program.

Should constness propagate to member pointer pointees?

I have a member function which is declared const and modifies data via a pointer. This seems misleading. Should I remove the const keyword?
I would like to know how others handle this situation in their code. Do people just add a comment to clarify what is going on? Do they not add the const keyword to the member function? Maybe something else completely?
Any advice is appreciated.
You have essentially two choices:
Deep constness:
class Foo
{
T * ptr;
public:
T & operator*() { return *ptr; }
T const & operator*() const { return *ptr; }
T * operator->() { return ptr; }
T const * operator->() const { return ptr; }
};
Shallow constness:
class Foo
{
T * ptr;
public:
T & operator*() const { return *ptr; }
T * operator->() const { return ptr; }
};
It's really up to you, and to the purpose of your class. If the class is a smart pointer, it would seem reasonable to have shallow constness semantics, since the class is supposed to be as similar to a raw pointer as possible (and you can of course have a constant raw pointer to a non-constant pointee).
Otherwise, you should ask yourself why you would be exposing access to a member pointer object at all. It's certainly possible that you want to give mutable access via constant references to your class, but I imagine those are special and rare circumstances. There shouldn't really be that many raw pointers in your code in the first place. Returning a deeply-const reference by dereferencing a pointer should be fine, but usually in better encapsulated "getter" functions which hide the fact that there is a pointer inside your class, like T const & get() const { return *ptr; }.
Generally, yes. Its deceptive to modify something you are declaring constant, even though you can do it.
If someone uses your code and sees const, they expect const. Modification, even though sensible to you, might cause them severe problems -- even crashing a program.
Consider a std::vector<Blah> member versus a Blah* member used to implement a dynamic array. Most often it makes sense to replace the latter with the former. With the Blah* memeber a const method is allowed to modify the data in the array, while with the std::vector<Blah> member the const method is not allowed to modify data there.
Also consider a matrix class with an indexing method that returns a proxy that allows assignment to an element. Assigning via the proxy changes the matrix, not the proxy object itself. Thus, the proxy object’s assignment operator can be (and should be) const, in order to impose the most constraints possible on its effect, while its primary job is to modify things.
That’s another example that the design level is different from the coding level.
In the first example, with a member array, const was all about expressing a design level constraint, but in the second example, with the assignment proxy, const was all about expressing a coding level constraint.
These usages are not incompatible, however. The key idea is to provide a reader of the code with as many constraints as possible (because that greatly reduces how many varying things that must be considered to understand or deal with the code). Upshot: add const wherever you practically can.

Implications of using an ampersand before a function name in C++?

Given the example:
inline string &GetLabel( ) {
return m_Label;
};
Where m_Label is a private class member variable.
The way I think I understand it, this function will return a reference to the variable m_Label. What would be the implications of using this throughout my program and would it be a better to just return the value, instead of the reference? Thank you!
The ampersand isn't before the function name so much as it's after the return type. it returns a reference to a string.
The implication of this is that a caller of this function could modify the value of m_label through the reference. On the other hand, it avoids copying the string. You might want the reference and the function to be const, like so:
inline const string& GetLabel() const
{
return m_Label;
}
Best of both worlds. You avoid the copy, but callers can't change your object.
It returns a reference to the private member.
There are many cases where this is desirable, but some care should be taken.
IMO it's generally not a good idea to return a copy of an internal object that is not an integral type, for overall performance reasons. Yes I know, premature optimization is not good, but this is not really optimization, it's just a good performance practice that allows the caller to determine the performance implications; if it wants a copy, it can just not declare the variable that it's assigning it to as a reference.
There are 2 general rules of thumb I use here:
1) If you don't want the caller to be able to modify the private object directly, declare the return value as a const reference:
inline const string& GetLabel() const{ return m_Label; }
2) A caller should never store the reference returned from a class method, it should only be used locally where the parent object is guaranteed to be in scope.
If for some reason you need callers to be able to store a reference to your internal objects, use smart pointers instead.
Returning a reference means that the calling code can modify the value of your member variable after you return. That's very dangerous, unless you intended for that to happen.
Better is a const reference, or return by value (without the &).
One implication is that if the enclosing object is destructed, the reference becomes invalid:
Object* o = new Object;
string& label = o->GetLabel();
delete o;
// label becomes a dangling reference here.
Another implication is that a caller may modify the string. You can remedy that by returning a const reference.
You're are correct. It's a reference to the string member.
The implication will be that if a caller were to assign a value or otherwise modify the returned string that they would also be modifying the member variable. If this is not the intent you may want to return a copy by value to avoid breaking encapsulation.

What can a 'const' method change?

C++ methods allow a const qualifier to indicate that the object is not changed by the method. But what does that mean? Eg. if the instance variables are pointers, does it mean that the pointers are not changed, or also that the memory to which they point is not changed?
Concretely, here is a minimal example class
class myclass {
int * data;
myclass() {
data = new int[10];
}
~myclass() {
delete [] data;
}
void set(const int index) const {
data[index] = 1;
}
};
Does the method set correctly qualify as const? It does not change the member variable data, but it sure does change the content of the array.
What can a 'const' method change?
Without explicitly casting away constness, a const member function can change:
mutable data members, and
any data the class has non-const access to, irrespective of whether that data's accessible:
via member variables that are pointers or references,
via pointers or references passed as function arguments,
via pointers or references returned by functions,
directly in the namespace or class (for static members) containing it.
These restrictions apply to operations of data members and bases (in the OO sense) too. More explicitly, a const member function operating on *this's data members or bases, when they're of class/struct/union type, can only call their const member functions (if any), and can only write to their mutable data members (if any).
(A const member function can also change any non-const local variables and by-value parameters, but I know that's not what you're interested in).
const data members can call other const member functions, which will have these same abilities and restrictions.
Eg. if the instance variables are pointers, does it mean that the pointers are not changed, or also that the memory to which they point is not changed?
It means the pointers can't be (easily/accidentally) changed. It does not mean that the pointed-to memory can't be changed.
What you've stumbled on is the logical incorrectness of a const function changing pointed-to or referenced data conceptually owned by the object. As you've found, the compiler doesn't enforce the const correctness you may want or expect here. That's a bit dangerous, but means constness doesn't need to be explicitly removed for pointers/references to other objects which may be changed as a side-effect of the const function. For example, a logging object. (Typically, such objects are not logically "owned" by the object whose const function is operating on them.) The key point is that the compiler can't reliably distinguish the type of logical ownership an object has over pointed-to data, so it's got to guess one way or the other and allow the programmer to either override, or not be protected by const-ness. C++ forgoes the protection.
Interesting, I've heard Walter Bright's D language flips this default, making pointed-to data const by default in const functions. That seems safer to me, though it's hard to imagine how often one would end up needing to explicitly cast away constness to allow wanted side-effects, and whether that would feel satisfyingly precise or annoyingly verbose.
Most succinctly, it means that the type of this is const T * inside const member functions, where T is your class, while in unqualified functions it is T *.
Your method set does not change data, so it can be qualified as const. In other words, myclass::data is accessed as this->data and is of type int * const.
There are two aspects to this question:
what does const mean to the compiler?
how does const apply when it cannot be validated by the compiler?
Question 1
The first is rather simple. The compiler validates that no data members are modified (unless they are qualified as mutable). It validates this recursively: for any user-defined types, it checks that no non-const methods are invoked. For built-in types, it validates that they are not assigned.
The transformation for pointers is T* to T*const (const pointer), not const T* (pointer to const). This means that the compiler does not validate that the object pointed to is not modified. Obviously, this leads to question 2.
Question 2
How does const apply when not validate by the compiler? It means whatever it should mean to your application. This is usually referred to as logical const. When to use const with respect to logical const-ness is subject to debate.
const when applied to a method means:
This means that the state of the object will not be changed by the method.
This means any members that are part of the objects state can not be modified, nor can any functions that are not also const be called.
As this relates to pointers. It means the pointer (if it is part of the state) can not be changed. But the object the pointer points at is part of another object so that means you can call non cost methods on this object (as it is not part of the state of this object).
const basically prevents changing the class instance members' values inside the function. This is useful for more clearer interface, but pose restrictions when using inheritance for example. It's sometimes a little bit deceiving (or a lot actually), as in the example you posted.
const would be most appropriate for Get functions, where it is obvious that the caller is reading a value and has no intentions of changing the object state. In this case you would want to limit the inherited implementations as well to adhere to constness, to avoid confusion and hidden bugs when using polymorphism.
For example
class A{
int i;
public:
virtual int GetI() {return i;};
}
class B : public A{
public:
int GetI() { i = i*2; return i;}; // undesirable
}
changing A to:
virtual int GetI() const {return i;};
solves the problem.