12

What are the known shortfalls of const in C++ and C++0x?

sbi
  • 219,715
  • 46
  • 258
  • 445
Puppy
  • 144,682
  • 38
  • 256
  • 465

12 Answers12

58

The only thing wrong with const is that it is seriously underrated by to many developers. It's one of the best tools in C++'s toolbox, very sharp, and yet not dangerous to cut yourself with.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 5
    "But what made those who had laughed at me stop laughing was when, after several days, it became obvious that some several very hard to reproduce bugs which a few of my coworkers were desperately looking for had disappeared." (from [chat](http://chat.stackoverflow.com/transcript/message/254713#254713)) – Fred Nurk Jan 18 '11 at 06:44
50

The main problem is that you have to write it. It should be the default, and all mutable variables or parameters should be specified explicitly.

Philipp
  • 48,066
  • 12
  • 84
  • 109
  • 6
    Can’t upvote this enough. Even though (or because) it would wreak havoc with most programmers’ preconceptions. – Konrad Rudolph Jan 12 '11 at 14:55
  • 5
    Except for two things: The majority of variables are created to be written to. Having them all read only by default creates more work. Also the C and C++ language philosophy is that the programmer knows what they're doing and the language shouldn't prevent you from doing what ever you want. – Jay Jan 12 '11 at 18:28
  • 2
    @Jay “The majority of variables are created to be written to.” – *Once*. Rarely do I change the value of a variable after its first assignment (containers are kind of an exception). – Konrad Rudolph Jan 12 '11 at 22:33
  • 3
    You're never going to find that one way or the other is better. Some objects you create for reading/writing (loop counters, iterators, containers, streams, most strings) and others you only assign to once -- actually, I can't think of any examples for that one. Why are your one-time writes not constant initialisations? – Lightness Races in Orbit Jan 13 '11 at 10:44
  • 1
    wow, i totally missed your answer when i wrote my own (now deleted, was saying the same). One reason I deleted mine is that I can't imagine it's a sane default for local variables. But I definitely can imagine it's sane for by-value parameters. So maybe making parameters const by default, unless you put the "mutable" keyword? – Johannes Schaub - litb Jan 13 '11 at 10:55
  • 1
    Many class member variables are initialised only in the constructor and then never change in the lifetime of the class. I have actually sometimes changed my policy to make these const and public, rather than non-const, private with public getters. I know many people don't like the concept of public members and if they are non-const I agree but if they are I don't see the issue. – CashCow Jan 13 '11 at 11:04
  • 1
    @Joh: I usually have `const` local variables, too. Of course, the issue there is much smaller: it doesn't help with regard to const-correctness, but it helps finding errors. – Philipp Jan 13 '11 at 13:18
  • @Philipp: Phew, I'm glad I'm not the only one making local variables `const`. `:)` – sbi Jan 13 '11 at 13:52
  • -1 from me I'm afraid. As litb suggests it would be a sensible default for function parameters, but it's rare for me to need a local variable whose value remains fixed after its initial assignment. C++ is an imperative language; there's no shame in assigning twice :) – j_random_hacker Jun 12 '11 at 16:20
  • @j_random: C++ is a multi-paradigm language and, for example, increasingly supports functional programming. I find myself rely on mutable data less and less over the years, and make everything `const` by default. – sbi Jul 18 '14 at 21:38
18

What's wrong with const is that many programmers don't seem to be able to understand it completely, and a "half-const-correct" project simply does not work. This is what you need to know:

  1. Foo vs. const Foo (or Foo const)
  2. Foo& vs. const Foo& (or Foo const&)
    • references-to-const bind to all kinds of things, while references-to-non-const don't
  3. Foo* vs. const Foo* (or Foo const*)
    • pointer variables can also be Foo* const and const Foo* const (or Foo const* const)
  4. void Foo::mutator() vs. int Foo::accessor() const
    • but pointer members inside const member functions still point to non-const objects
    • so we can accidentally return non-const data from a const-function
  5. iterator vs. const_iterator
    • iterator variables can also be const iterator and const const_iterator

Migrating to C++ from a language that has no const concept is quite hard, any many fail to see the point.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • Nit-picking: references to objects can't be const, but references can reference const objects. (Most people know what you mean, but the distinction is more important with pointers, where we have both const pointers and pointers to const.) – Philipp Jan 12 '11 at 14:50
  • 1
    @Phil: Right, I changed the relevant parts from English to C++ ;-) – fredoverflow Jan 12 '11 at 15:02
  • 3
    That many programmers don't understand it is not a problem with `const`. It's a problem with the programmers, and the seriously pathetic state of programming education across the Western world. – Lightness Races in Orbit Jan 13 '11 at 10:45
  • @Chris: Thanks for catching that! – fredoverflow Jan 13 '11 at 10:54
  • The question doesn't ask how to use const. I already know how to use const. The question relates to the downsides of const. – Puppy Jan 13 '11 at 10:59
  • Good breakdown of the uses of const Fred, but where can I find a good reference on the given use cases ? I am new to the idea that const can save a lot of bugs, but I need a thorough understanding of it prior to use. Hope someone can help with a good tutorial/links – OverTheEdge Feb 15 '13 at 10:40
  • 1
    @OverTheEdge I suggest you buy "Effective C++" (3rd Edition) by Scott Meyers. Item 3 devotes 10 pages to the topic. – fredoverflow Feb 15 '13 at 12:25
16

The two main issues that I have seen frequent complaints about in newsgroups, are

I think the latter could/should be supported by the language.

Possibly in conjunction with support for covariant member function implementations, because both need some way to pick up the type of the this pointer.

A third issue is that

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 2
    Which Microsoft APIs are not-const-aware? IIRC the Windows API uses LPCTSTRs, MFC uses const correctly IIRC, etc. I can't think of any. – Rup Jan 12 '11 at 14:37
  • @Rup: would need a lot of research going through them all. i'm reporting what people have complained about, but i can confirm from own experience. if there were just a few problematic functions then perhaps i'd remember names, but it's all over. – Cheers and hth. - Alf Jan 12 '11 at 14:41
  • 12
    @Rup: Many Microsoft APIs don't use a lot of const. When was the last time you saw a const HANDLE? Or a const IDirect3DDevice9*? Microsoft uses const for strings and that's about it. – Puppy Jan 12 '11 at 15:16
  • 4
    @DeadMG: A `HANDLE` is an opaque object that you cannot modify anyway without using API functions. For the COM interfaces they use attributes like `[in]` and `[out]`. And for most other C types, they do use `const`. – Philipp Jan 12 '11 at 17:30
  • 1
    Perhaps it's not purely Win32 but COM's `BSTR` can't be a `const` (since it's a `typedef` which doesn't allow putting the `const` on the correct part of the type). – Motti Jan 13 '11 at 07:17
  • 1
    @Philipp: You can modify the `HANDLE` just fine; you can't easily modify the thing that it ends up pointing to. `HANDLE` being `const` would help to avoid people forgetting that. – Lightness Races in Orbit Jan 13 '11 at 10:45
  • 3
    +1 for "The need to define both const and non-const version of a method." ! – Johannes Schaub - litb Jan 13 '11 at 10:57
  • @litb Ah I missed that on my first read. +1 from me too for that. – Lightness Races in Orbit Jan 13 '11 at 10:58
  • @Tomalak note that "@name" will notify the user only if replace(, " ", "") starts with "name". So "@Joh" would notify me of your comment and "@JohannesSchaub" would too. But "@litb" won't :( – Johannes Schaub - litb Jan 13 '11 at 11:55
  • @JohannesSchaub: Maybe I was hiding :P (OK) – Lightness Races in Orbit Jan 13 '11 at 14:50
  • What do you mean by const not propagating to owned objects? If you hold a constant reference to any structure, all members are also considered constant. You even have the keyword `mutable` to allow implementation of caches (i.e. it doesn't impact the output if this variable changes). – PierreBdR Aug 24 '11 at 07:25
  • 1
    @PierreBdR: I think what they mean is that a member `int *` is seen as `int * const` (and not `int const *`) from within a const member function. As a result what it points to can still be changed. – Richard Corden Aug 24 '11 at 12:15
  • @Alf: Can you add some links relating to the "need to add both const and non const versions"? – Richard Corden Aug 24 '11 at 12:18
  • @Richard: the **[C++ FAQ](http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.12)** is a good starting point. I did a little googling and discovered that many other resources are outdated, including a GOTW by Herb Sutter, linked to from **[the Wikipedia page on const correctness](http://en.wikipedia.org/wiki/Const-correctness#const_and_immutable_in_D)**. Herb's **[GOTW](http://gotw.ca/gotw/006.htm)** advises "When using return-by-value for non-builtin return types, prefer returning a const value", reasonable in its day, but which with Mojo or C++0x prevents efficient "moving". – Cheers and hth. - Alf Aug 24 '11 at 14:08
3

The problem with const is the programmers that use it incorrectly our inconsistently

John Dibling
  • 99,718
  • 31
  • 186
  • 324
3

One thing that is "wrong" is that you cannot convert T** to T const * const * which should be allowed because it is not dangerous. Not allowing T** to convert to T const ** is correct, that conversion is not valid.

I have sometimes mentioned that const actually is a cheap way to "split" your interface into read-only methods and write methods. It would probably be impractical in C++. It would be more practical in Java to have ReadOnly versions of collections though, where they don't have const and where their collection types are more object-orientated.

const-ness does not propagate: The issue here is that if I pImpl my class, the constness is not "checked" by the compiler, i.e. my interface class can have a "const" method call a non-const method on the pImpl and the compiler won't complain. That is because the only thing my const method is guaranteed not to do is change the pointer to point to a different object, and my pImpl is never going to change. It could even be a const pointer (not pointer to const).

The lack of proper co-variance between shared_ptr<T> and shared_ptr<const T> might be an issue too, although in general I have seen that not to be the problem, but that developers usually typedef their shared_ptrs and rarely typedef the shared_ptr to const. And they will sometimes pass const shared_ptr<T> & and think that they are passing a shared pointer to a const T (as with const T*) which they are not.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • "cannot convert `T**` to `T const * const *`" - this part is wrong (at least with the compilers i use); i recall it was an annoyance in C but C++ got it right. – anatolyg Jan 12 '11 at 17:31
  • 1
    you can have const-checking for pimpl class, no big deal ... for example look at Qts use of pimpl classes.. (they use 2 private pimpl-accessor functions (const/nonconst) for accessing the pimpl object, if you want to make sure you dont access the pimpl object without using these functions (und thus circumventing the const-check), you can use a fully opaque `void*` for the pimpl object, and add some reinterpret_casts to the acessor functions (look at the implemtation of i.e. Q_DECLARE_PRIVATE) – smerlin Jan 13 '11 at 11:04
  • 2
    @anatolyg: You are correct. The first paragraph doesn't describe something "wrong" with `const` because it's not true. See 4.4 [conv.qual] / 4 of ISO/IEC 14882:2003. – CB Bailey Jan 13 '11 at 11:49
1

"issues"?

If you aren't going to be modifying the value of a passed pointer (it is used purely for pass-by-reference input to a function), mark it is const. Ditto if the value of a particular variable will not change after its initialization. If a function is safe to call on a const class instance, mark it const too. The more items are correctly annotated const, the less likely you are to inadvertently make a mistake and the more optimizations the compiler will be theoretically able to perform in the absence of complete knowledge (such as when compiling with only function prototypes available).

Modern versions of gcc have support for warning when you try to cast a const variable to a non-const one. I suggest you leave those warnings enabled.

The only thing to watch out for is exactly what you are marking const; const char * foo() is not the same as char * foo() const.

Borealid
  • 95,191
  • 9
  • 106
  • 122
  • I'm not asking how to use const. I already know how to use const. I was looking for *problems* with const. – Puppy Jan 12 '11 at 16:59
0

const is great. const is important. const-correctness is necessary condition for an API to be good.

Yet there are two issue I've had with const, repeatedly.

  • There's no way to mark a variable as const retroactively. You must either declare a variable code, in which case you have to initialize it immediatly. What if the initialization code contains an if, though? You have the choice of either omitting the const (undesirably), using operator ? instead of if (harms readability). Java get's that right, BTW - const variables don't have to be initialized right away, they just have to be initialized before they're first read, and they have to be initialized in all branches of an if.

  • There's no way to specifiy that an object passed by reference to a function won't change for the duration of the function call. const T& t does not mean that the object pointed to by t won't change, but only that the reference t cannot be used to change it. The compiller still has to assume that any function call which it does not see into might change the object. It some cases, that prevents quite a few optimizations.

fgp
  • 8,126
  • 1
  • 17
  • 18
0

Another problem that has not been mentioned yet is the possibility for a badly designed interface to subvert const (even in the absence of casts).

Example:

class TreeNode {
public:
    TreeNode& getParent() const { return *parent_; }
    TreeNode& getLeft()   const { return *left_;   }
    TreeNode& getRight()  const { return *right_;  }
private:
    //TreeNode has a pointer to the Tree to enable navigation of the tree.
    //Assume that other design constraints mean that this must be a pointer 
    //rather than a reference.
    TreeNode* parent_;
    TreeNode* left_;
    TreeNode* right_;
};
//This function demonstrates the ability for const to be subverted.
TreeNode& remove_const(TreeNode const& toRemoveConstFrom) {
    TreeNode& parent(toRemoveConstFrom.getParent());
    TreeNode& leftChild(parent.getLeft());
    TreeNode& rightChild(parent.getRight());
    return &toRemoveConstFrom == &leftChild ? leftChild : rightChild;
}

The intransitive nature of const means that it is possible to have an interface where a non-const reference to an object can be obtained from a const reference to an object. This is something to be careful of when designing interfaces.

Mankarse
  • 39,818
  • 11
  • 97
  • 141
0

Most of the answers below state things such as "what is wrong with const is that X people do Y". Those are not answers but symptoms. Those are not things wrong with const. There's barely any wrong thing with const... Those are things wrong with people who can't RTFM.

Luis Machuca
  • 1,047
  • 9
  • 16
-1

One problem is that the language also lets you const_cast it away, which defeats the purpose of using const in the first place.

Dave
  • 14,618
  • 13
  • 91
  • 145
  • not really as you can only explictly cast it away and you obviously need to be able to const_cast to support legacy APIs – jk. Jan 12 '11 at 14:37
  • 6
    Not this again... `const` would be useless **without** the ability to cast it away, because in real world code, you have to deal with APIs written by programmers who don't understand const correctness (or way back in a time were `const` wasn't part of C++ yet). – fredoverflow Jan 12 '11 at 14:38
  • @FredOverflow I see... I hadn't ever run into that situation before. Thanks for the clarification. – Dave Jan 12 '11 at 14:50
  • @jk the problem I have run into is when I have a const function and another developer const_casts it away, which defeated the whole point of making it const in the first place. At least that is my recollection. – Dave Jan 12 '11 at 14:52
  • 4
    @FredOverflow: but maybe it would be a good thing if those programmers were hunted down and killed by people who can no longer work around their broken code by const-casting? ;-) Regarding ancient code - something can still be a shortfall of the language even if the motivation for the shortfall is to support pre-standard code and/or conventions. For instance you might think that the automatic conversion to `void*` is a shortfall of C++: it's needed for e.g. `std::memcpy`, but in the context of C++, functions shouldn't be designed to silently accept any pointer regardless of type-safety. – Steve Jessop Jan 12 '11 at 14:53
  • 6
    @Dave: *Why* did the developer cast it away? Most casts are evil and/or unnecessary. If you don't trust programmers, C++ is the wrong language. – fredoverflow Jan 12 '11 at 14:56
  • @FredOverflow it was out of necessity to get a particular behavior that he wanted, but at the expense of everything else. The change never made it through, though. :) – Dave Jan 12 '11 at 15:00
  • 1
    @Steve: How would killing them *now* solve the problem? Let the damn terminators do the job thoroughly and wipe the non-believers from history! – fredoverflow Jan 12 '11 at 16:15
  • 2
    No, although `const_cast` is legal, writing to an object-created-as-`const` (even if you've casted away the constness on the _variable_) is not legal. – Lightness Races in Orbit Jan 13 '11 at 10:48
  • (That said, sure, it would be nice if `const_cast` didn't even exist. It's a hack and can easily lead to the illegality I describe above.) – Lightness Races in Orbit Jan 13 '11 at 10:50
-2

One thing is that it is still possible subvert it. i.e. it is still legal to do something like this:

void foo(const inst& x)
{
   const_cast<int&> x = 3;
}

You can even use things like memset to subvert it without an explicit const_cast.

This is a trade-off between having the compiler enforce const and allowing some flexibility for non const aware interfaces.

Which leads to another limitation, in that it has not been universally embraced, which is in part due to another problem, which is that using const is an all-or-nothing proposition. If you start using it, you will need to propagate it throughout your code base.

JohnMcG
  • 8,709
  • 6
  • 42
  • 49
  • 5
    No, this is possibly Undefined Behaviour (if the original object was created `const`) and in the context of this function must be assumed to be. A `const_cast` is legal, but writing to an object created as `const`, whether you've casted away the constness or not, is illegal. – Lightness Races in Orbit Jan 13 '11 at 10:47
  • Well, yes, but if you think as const in terms of a contract or promise that I won't change it (even if it was non-const before), then it is possible to break it. – JohnMcG Jan 13 '11 at 15:22
  • But you can make promises in form of comments in every language and then subvert them. `const` however is a non-comment/builtin tool that forces you to not break the contract _by default_, whereas in non-const-correct languages you may accidentally change something documented as invariant. – Sebastian Mach Aug 25 '11 at 07:13