As far as I read here and there, const
should be used when possible. However, I have a case that always bothers me.
Should I mark a member function as const
when it does not alter any member variable values but it is not conceptually a const
function?
For example:
class Engine{
public:
int status;
};
class Car{
public:
void start() const{
engine_->status = 1;
}
private:
std::unique_ptr<Engine> engine_;
};
The compiler will accept the constness of start()
since engine_
as a pointer did not change. However, It seems so unrealistic, at least IMO, that a function called start
in a class called Car
is a const
one!
The example was just a quick one. Usually, some internal states of the Car
class should be updated accordingly making const
keyword non-feasible. However, the mini example was just to illustrate my idea.
One simple metric for whether a function should be const
is this:
Type a{...};
Type b{...};
bool comp1 = a == b;
b.some_func(...);
bool comp2 = a == b;
If comp1
and comp2
can ever be different, then some_func
is not const
.
Obviously, not every type has an operator==
overload, but most have at least the conceptual idea of what you would test to see if they're equal. Different Car
instances with different engine
states would be unequal. Therefore, a function that changes the engine
state is not const
.
In your case compiler allows you to make start()
const due to imperfect propagation of constness through pointers. If you replace your pointer with object of type Engine
your question will disappear. So answer is no, it should not be const
in this case as using Engine
as a smart pointer or instance is internal details and should not affect public interface of class Car
.
As far as I read here and there, const should be used when possible.
This statement is way too generic, and as with any generic suggestion should not be used formally in every case.
In your example, you might want std::experimental::propagate_const:
class Car{
public:
void start() { engine_->status = 1; }
private:
std::experimental::propagate_const<std::unique_ptr<Engine>> engine_;
};
Then your start
can no longer be const
.
The meaning of const
can vary.
Something is const
if it preserves ==
.
Something is const
if your type follows reference semantics and it doesn't change what is referred to.
Something is const
if it can be used on any rvalue or lvalue in sensible ways.
Something is const
if it is safe to use from multiple threads.
Something is const
if it compiles as const
.
Something is const
if whatever state the object claims is internal is not mutated by it.
All of these are reasonable rules to decide if a method or argument is or is not const
.
A thing to be extremely careful of is to know the difference between T const*
and T*const
, and don't accidentally hse top-level const as an internal const. It isn;t const iterator
it is const_iterator
. It isn't const gsl::span<int>
, it is gsl::span<const int>
. It isn't const unique_ptr<T>
, it is unique_ptr<T const>
.
On the other hand, vector
is a value semantics typr; it pretends its buffer is a part of it (even though this is a lie). It isn't vector<const T>
, it is const vector<T>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With