2

I was wondering if it is necessary to put const in the parameters of a function and in the parameters to achieve const correctness.

My understanding is const correctness is a promise not to change the variable.

For example are:

bool operator==(const rational<T>& rat);
bool operator!=(const rational<T>& rat);

and

bool operator==(const rational<T>& rat) const;
bool operator!=(const rational<T>& rat) const;

equivalent?

I was thinking there were mainly because if you don't change the parameters, they you don't change anything in the class or are they different because you can change the values of the public/private members, but not passed in parameters.

Please correct my terminology if used incorrectly.

GrandFleet
  • 809
  • 2
  • 10
  • 25
  • 2
    Very related: [What are the basic rules and idioms for operator overloading?](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading) – πάντα ῥεῖ May 17 '19 at 17:59
  • "const correctness is a promise not to change the variable" -- a promise by what? to what? I think it means that marking a parameter or function as const is a 'command-to-the-compiler' to report an error when the code violates. – 2785528 May 17 '19 at 18:06
  • A promise by the programmer to the programmer. And if they break that promise, the compiler can call them on it. – user4581301 May 17 '19 at 18:10

3 Answers3

5

const at the end applies to this (making it pointer-to-const), meaning the member function will not modify the object on which it is called

class Cls {
 public:
  void f() {
    ++x_; // no problem
  };

  void g() const {
    ++x_; // error, can't modify in const member function
  };

 private:
  int x_{};
};

In your example, you want to say both that the parameter is const, as well as this. In lhs == rhs, lhs is treated as const only if you have the trailing const, so you are right to use

bool operator==(const rational<T>& rat) const;
bool operator!=(const rational<T>& rat) const;

(though you should probably be omitting <T>)

Further, if you omit the trailing const, you would not be able to compare with a const object on the left

const rational<int> a;
const rational<int> b;
if (a == b) { // error if you don't have the trailing const on operator==
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
2

I upvoted Ryans and a now retracted answer but to illustrate it:

bool operator==(rational<T>& rat);

Both rat and this may be changed.


bool operator==(const rational<T>& rat); // equivalent to the below
bool operator==(rational<T> const& rat); // equivalent to the above

Only this may be changed.


bool operator==(rational<T>& rat) const;

Only rat may be changed.


bool operator==(const rational<T>& rat) const; // equivalent to the below
bool operator==(rational<T> const& rat) const; // equivalent to the above

Neither rat nor this may be changed - which for this specific operator is the expected behaviour.


As user4581301 pointed out, making everything as const as possible makes it possible for the compiler to consider using the function in more cases than with only const instances. It'd happily use it for non-const's too. In addition to that, the optimizer may use the const information. It may make the resulting executable faster, and const may even make it compile faster. Both since it narrows down the possible considerations involved, both in run-time and during compilation.

If in doubt, start const.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    Prefer the all `const` version until forced to do otherwise. It is logically more consistent with most people's expectations, that a comparison will not change the values of the compared, and it may help prevent the insertion of future bugs should the function be modified to accidentally change a value. In addition since `this` is `const` you can use the function on modifiable and non-modifiable instances. – user4581301 May 17 '19 at 18:16
  • @user4581301 Indeed! Added a little note. – Ted Lyngmo May 17 '19 at 18:18
0

The placement of the const keywords has different meanings depending on where they are put. The const keyword before the parameter specifies that the parameter is a constant, meaning that the value of it cannot be modified. If the const keyword is instead put at the end, it means the value of the variable from where the function is called is constant and thereby cannot be modified.

Here is an example:

class Foo
{
public:
   void First(const float& something) const;
   void Second(const float& something);
   void Third(float& something) const;
}

In the first function, the variable of type Foo from which the function is called as well as the variable something cannot be modified by calling the function.

In the second function, the variable of type Foo from which the function is called can be modified by the function. This means it could change its members and thus alter the variable.

In the third function, the variable something can be modified but not the variable of type Foo from which the function is called. This is for example useful in cases where you want to modify a value with regards to the members of a variable of type Foo without actually altering that value, only altering the variable passed in as a reference.

Lorago
  • 161
  • 8
  • "_The const keyword before the parameter specifies that the parameter is a constant,_" - if no other parameters exists before it, right? Perhaps worth mentioning if you're being specific. `parameter <- const` is the way it goes. – Ted Lyngmo May 17 '19 at 19:31