19

As I try to apply const-correctness for my own code, I often have to add const qualifications to function definitions in other modules (written by other programmers) in order to use those functions in my own code. (see here on back-patching const-correctness)

I always thought that if everything compiles fine, this could impossibly lead to broken functionality as const labels only matter at compile time.

Yet, the other day one of my colleagues insisted that I should rerun all automated tests before I commit code with added const labels, where I thought it was sufficient that such code compiled.

Does he have a point? Is there a way that applying const-correctness could break existing functionality ?

Edit: It is maybe important to note that, generally, I only have to do this for pointer parameters of functions (e.g. Something getSomething(Object* pObj) to Something getSomething(const Object* pObj). I do not change return types or method constness as this is not a problem for client code.

mr_T
  • 2,571
  • 3
  • 22
  • 39

3 Answers3

15

They do indeed have a point.

  1. If you cast away constness from a variable that was originally const, then the program behaviour is undefined. You risk introducing that into your code if you add const qualifications.

  2. You might unintentionally switch function overloading for function overriding.

  3. Passing an anonymous temporary to a function that takes a const reference is defined, but if the function takes a non-const reference then the behaviour is undefined. Many compilers permit the non-const (possibly accidentally although some even go as far as calling it an extension). Ostensibly, you're doing everyone a favour in fixing this, but you might be removing an undefined-behaviour construct that is relied upon at runtime.

  4. The C++ standard does not insist that sizeof(T*) == sizeof(const T*). Your class' v-table layout could be different on your making a non-const to const parameter switch. Granted, it's unlikely, but you must test.

In summary, you must test these changes.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 3
    For case (3) such code should be ill-formed, not undefined. VC++ is broken. And which are compilers are included in "many compilers"? – Jonathan Wakely Nov 23 '15 at 11:26
  • I'm not disagreeing with your answer but, to clarify, does (1) imply that using `const_cast` always yields undefined behaviour? – Component 10 Nov 23 '15 at 11:33
  • Not at all: If the variable was originally non-const, and you receive it as const, you are allowed to cast it back to non-const. Of course, casting to const is always fine. – Bathsheba Nov 23 '15 at 11:34
  • 19
    You're being [challenged](http://stackoverflow.com/questions/33869832/could-it-be-the-case-that-sizeoft-sizeofconst-t#comment55503160_33869976) on point 4. – Avi Ginsburg Nov 23 '15 at 11:37
  • Hum. There's *no way* that the standard insists that sizeof(char*) is the same as sizeof(const char*). That's one counter-example. – Bathsheba Nov 23 '15 at 11:40
  • 5
    @Bathsheba "3.9.2.3. Pointers to cv-qualified and cv-unqualified versions (3.9.3) of layout-compatible types shall have the same value representation and alignment requirements (3.11)" – Joker_vD Nov 23 '15 at 11:46
  • I'm confident you're correct for non-PODs, by what about pointers to PODs? – Bathsheba Nov 23 '15 at 11:48
  • Why would you think so? Every type is layout-compatibile with itself. – Sopel Nov 23 '15 at 15:14
  • @AviGinsburg, In regard to the challenge, ElderBug's comment on the first answer is crucial. In short, the difference is not supposed to happen, per the rules, yet in practice, it sometimes does happen anyway. – donjuedo Nov 23 '15 at 17:12
  • @donjuedo I was just adding a reference, not taking a stance. Kind of like "this post is being discussed on meta." Since then, the comment that referred to this post has been deleted. I'll leave the comment for the (IMO helpful) cross reference. – Avi Ginsburg Nov 23 '15 at 17:21
  • Good call. It's good to know the rules, but code for reality ("defensive" coding). – donjuedo Nov 23 '15 at 17:29
  • 1
    So, case (1) only applies to `const_cast` and equivalents (which isn't mentioned by OP). It appears that (3) is wrong and so is (4), except insofar as your compiler is not a C++ compiler. Which leaves (2) -- accidentally removing a overload and replacing it with an override. Also missing is the possibility that a function dispatches to a different override after you `const`ify a parameter; while such override switches shouldn't change functionality, sometimes existing code sucks. – Yakk - Adam Nevraumont Nov 23 '15 at 19:09
  • All the more reason to test then. I've never come across a c++ compiler by this definition. – Bathsheba Nov 23 '15 at 19:10
  • @Bathsheba clang? gcc? – Yakk - Adam Nevraumont Nov 23 '15 at 19:25
  • No, they all have bugs. I'm struggling to find why you find this so objectionable. – Bathsheba Nov 23 '15 at 19:40
12

As const method may differ than non const one

class C
{
public:
    int& get() { return i; }    
    int get() const { return i; }
private:
    int i = 42;
};

you may have different bahavior:

C c;

auto&& p1 = c.get();
auto&& p2 = c.get();
assert(&p1 == &p2); // true

whereas

const C c;

auto&& p1 = c.get();
auto&& p2 = c.get();
assert(&p1 == &p2); // not true.
Jarod42
  • 203,559
  • 14
  • 181
  • 302
3

Yes.
One possible problem with adding consts is possible undefined behaviour when changing the variable later (in a situation where the compiler can't prevent it anymore).
What if f is const here?

float f = 1.0;
//do something with f
readFromBinaryFile((char *)(&f), sizeof(f));
//do another something with f

//...

void readFromBinaryFile(char *c, size_t s)
{
    //... fill c
}
deviantfan
  • 11,268
  • 3
  • 32
  • 49