23

As stated in book Effective C++: "Use const whenever possible.", one would assume that this definition: Vec3f operator+(Vec3f &other); would be better defined as Vec3f operator+(const Vec3f &other) const; or even better as const Vec3f operator+(const Vec3f &other) const;.

Or an example with 5 const keywords: const int*const Foo(const int*const&)const;

Of course, you should only include const where there can be one. What Im asking is it a good practice to use them whenever possible? While it does give you more error safe code, it can get quite messy. Or should you, for example, ignore pointer and reference const (unless you really need it), and use it only on the types itself, as in const int* Foo(const int* parameter)const;, so it doesnt end up too messy?

Additional info: http://duramecho.com/ComputerInformation/WhyHowCppConst.html

Thanks in advance!

user2340939
  • 1,791
  • 2
  • 17
  • 44
  • 9
    Putting a top-level const on the return type has lost its value in C++11, but other than that, yes, const-correctness helps. – chris Mar 09 '14 at 17:55
  • 3
    Pointer and reference `const` is **far** more important than value `const` when passed as function argument. When passing by value, you are passing a copy, unlike when you pass by pointer or reference. – lapk Mar 09 '14 at 17:58
  • Im only asking, if for example we have this definition: const int*const Foo(const int*const&)const;, taking in mind that in this situation we could use all of this consts (but is not a must, its optional for our test purposes!), should we do so, although it looks messy? – user2340939 Mar 09 '14 at 18:05
  • 6
    You should pass function parameters by const reference if the function doesn't modify them, looking messy is irrelevant. A member function should be const if it doesn't modify the object, looking messy is irrelevant. – Jonathan Wakely Mar 09 '14 at 18:06
  • 1
    @PetrBudnik, `const` on parameters passed by value is ignored completely, so it's not just _less_ important, it's unimportant – Jonathan Wakely Mar 09 '14 at 18:08
  • So if you use pointers and most of the time never change their value, should you declare all of them as const? Guess so... – user2340939 Mar 09 '14 at 18:13
  • @user2340939 If the pointer should gurantee not to change at all, yes. However in such a case, a reference is usually better suited. – Sebastian Hoffmann Mar 09 '14 at 18:18
  • 1
    @JonathanWakely What do you mean "ignored"? Try compiling `void foo(int const a) { a += 1; }`... If you are not using `const` on value parameter and change it by mistake, you are changing copy. Unlike with reference or pointer. So it's not that big of a deal not to use it in function declaration. But if you use it, it is not ignored, it is enforced by compiler even for copy. – lapk Mar 09 '14 at 18:24
  • @PetrBudnik, I meant in the context of the declarations in the OP's question, but I should have made that clear, sorry. – Jonathan Wakely Mar 09 '14 at 18:28
  • @JonathanWakely Well, I guess, we both understand the issue all too well to be able to explain it to each other clearly ;). – lapk Mar 09 '14 at 18:37
  • P.S. I suppose, you meant that in `T const & const` second `const` is redundant, because references themselves cannot be changed. – lapk Mar 09 '14 at 18:44
  • @JonathanWakely Looking messy is far from irrelevant. Humans are in charge of maintaining source code and whether you like it or not, messy code is more difficult and time consuming to maintain. There is a real cost to every character that exists in a source file and developers should be aware of that. – Lother Mar 09 '14 at 18:44
  • You shouldn't return `const Vec3f` - the person might want to write something like `a + b + c`. Also, it's considered better practice to make `operator+` a free function (i.e. not a member function), and have it delegate to the member function `operator+=` if necessary. – M.M Mar 09 '14 at 19:08
  • @Lother, I wasn't talking about the general case, I was responding to the OP's question. But in answer to your comment: taking a non-const reference parameter when the parameter is not modified **decreases** maintainability, because it makes it impossible to call with a temporary (making callers write extra code to create an lvalue from a temporary) and makes experienced C++ developers wonder _why_ it takes a non-const reference, and so wastes mental effort. There are more important things to consider than the number of characters in the source file. – Jonathan Wakely Mar 09 '14 at 22:20
  • @PetrBudnik, `T const & const` is ill-formed, references cannot have cv-qualifiers – Jonathan Wakely Mar 09 '14 at 22:22
  • @JonathanWakely Yes, because it is redundant - references can only refer to the same object during their lifetime. Anyway, I don't think there is any disagreement, really. – lapk Mar 09 '14 at 23:06

5 Answers5

8

C++ FAQ:

If you find ordinary type safety helps you get systems correct (it does; especially in large systems), you'll find const correctness helps also.

You should use const when you want to be sure not to change variable accidentally or intentionally. Some constants (globals and class static, strings & integers, but not variables with nontrivial constructor) can be placed in read-only parts of the executable, therefore result in segmentation fault if you try to write to it.

You should be explicit using const as a specifier on functions that follow this principle as well as on function arguments. If you don't have to change actual argument, make it const. This doesn't limit the possible usages of such function, but extends them, because now they might be used on const arguments, and on const objects.

In declaration

const int* foo(const int* const&) const;

every const means something different and yes, obviously it should be used if it is needed.

Summary

Using const increases type-safety of your program.

C++ FAQ:

[18.3] Should I try to get things const correct "sooner" or "later"?

At the very, very, very beginning.

4pie0
  • 29,204
  • 9
  • 82
  • 118
6

Using const whereever possible is generally a good thing, but it's a bad wording. You should be using const whereever it is possible and makes sense.

Above all else (it may, rarely, open extra optimization opportunities) it is a means to document your intention of not modifying something.

In the concrete example of a member operator+ that you have given, the best solution would not be to make everything const, but a freestanding operator+ which bases on member operator+= and takes one argument by value like so:

T operator+(T one, T const& two) const { return one += two; }

This solution works with T appearing on either side of the plus sign, it allows for chaining, it doesn't replicate code, and it allows the compiler to perform the maximum of optimizations.
The semantics of operator+ require that a copy be made, so you can as well have the compiler make it (the other one will be optimized out).

It would be possible to make one a const&, but then you would have to manually make a copy, which would likely be sub-optimal (and much less intellegible).

Damon
  • 67,688
  • 20
  • 135
  • 185
2

When using const you are telling the compiler something.

It can then use that information to check that you are not doing something that you should not be doing and also enables it to optimize the code.

So when using const it enables it to be smarter.

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
  • 2
    Using `const` almost never helps optimization, if the compiler can tell whether you're modifying an object or not then it can perform the same optimizations without needing `const`. See http://www.gotw.ca/gotw/081.htm – Jonathan Wakely Mar 09 '14 at 18:03
  • Once you start to use const it can also cause unnecessary creation and destruction of temporaries. eg Passing a const object to a non cost parameter when the function does not modify the object. You need to use it consistently to get the benefit. – QuentinUK Mar 09 '14 at 18:08
  • 1
    @QuentinUK I dont think so. Lets consider the situation: If you pass by value, a copy is always made if its a lvalue, whether its const or not, period. If you pass by reference though, you simply cant pass your object, because the compiler assumes that the object can be modified. So yes, if you want to pass your object nonetheless, you will have to create a copy (explicitely). But that is not a bug, its design. – Sebastian Hoffmann Mar 09 '14 at 18:16
  • I think I've seen this due to a "feature" of VS2010, http://stackoverflow.com/questions/7189420/one-vs2010-bug-allowing-binding-non-const-reference-to-rvalue-without-even-a-w – QuentinUK Mar 24 '14 at 12:58
0

I think that using const whenever possible is a good way to go. It adds implicit documentation to your code and increases type safety, as noted by the other answers.

It's nice to know that you can call const functions without having to worry about whether they change an object's internal state and that passing const pointers or references to something other object will keep it safe from encapsulation breakage. It also lets objects owning const references get the most value out of whatever that reference is pointing to.

Also, applying const from the get-go is a good idea because it can be a pain to add later. For example, if you have a class:

class A
{
public:

  //...

  int getCount()
  {
    return m_count;
  }

private:
  int m_count;
}

And another:

class B
{
public:

  //...

  int getInternalCount()
  {
    return m_a->getCount();
  }

private:
  A* m_a;
}

And you want to do:

void foo( const B* b )
{
  int count = b->getInternalCount();

  //-- Do something
}

This is a perfectly valid use case, but you then have to go and add const to multiple places to support it (B::getInternalCount() and A::getCount()). If you do it from the very start, it's actually quite easy to use const and it makes the design much more obvious to other developers. If you're working on your own, it still helps a lot, but it's not as much of a concern. When working in a team, though, I found it really helped when we started enforcing const and retrofitted some of our legacy code to adhere to it more.

Long story short: I recommend always using it.

RymplEffect
  • 151
  • 4
-1

The original purpose of const is to deal with problems, which arise when you use magic numbers and hard-coded values in general.

A bit stupid, but picturesque example to show the point. Such code is error-prone and inconvenient.

c1 = 3.14159 * diameter1;
c2 = 3.14159 * diameter2;

You'd rather be better defining Pi as a constant.

const double PI = 3.14159;
c1 = PI * diameter1;
c2 = PI * diameter2;

Compared to variables constants have "priviliges". In your example it makes sense to pass other vector by const reference, because thanks to this function will accept temporary (rvalue) as its parameter.

Vec3f operator+(const Vec3f &other) const;

// Would not be possible with Vec3f operator+(Vec3f &other) const;
Vec3f v = u + Vec3f(1, 0, 0);

With constexpr you can use constants in contexts such as for example static array allocation

constexpr std::size_t LENGTH = 10;
int arr[LENGTH];

So it's not to prevent you from accidentally setting some random value (you would have to be exceptionally stupid programmer to make such mistakes). It does not make sense to use it whenever possible, but only when you want to specify a constant.

This is not the first time I see weird statements from "Effective C++".

mip
  • 8,355
  • 6
  • 53
  • 72