15

Take note of the following C++ code:

#include <iostream>
using std::cout;

int foo (const int);

int main ()
{
   cout << foo(3);
}

int foo (int a)
{
   a++;
   return a;
}

Notice that the prototype of foo() takes a const int and that the definition takes an int. This compile without any errors...

Why are there no compilation errors?

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
Alerty
  • 5,945
  • 7
  • 38
  • 62
  • Please look up about pointers. – alternative Sep 09 '10 at 23:05
  • 14
    @mathepic: What does this code have anything to do with pointers? – casablanca Sep 09 '10 at 23:07
  • @casablanca Everything - It seems that hes mixing up const int and int as being like int and int *. – alternative Sep 09 '10 at 23:18
  • 1
    Vaguely related warning: If you ever find yourself using C++/CLI, beware. While this _should_ work in C++/CLI, [it doesn't always](http://stackoverflow.com/questions/2413097/c-vs-c-cli-const-qualification-of-virtual-function-parameters). – James McNellis Sep 09 '10 at 23:18
  • 4
    @mathepic: Not at all, the question is why the prototype and actual definition are allowed to have different parameter types. – casablanca Sep 09 '10 at 23:20
  • @casablanca I interpreted as the person thinking that const int is different from int because int can be modified. Which is wrong, and pointers are the reason. – alternative Sep 09 '10 at 23:24
  • 3
    @mathepic: no, he's mxing up const int and int as being like apples and motorcycles. Pointers have nothing to do with this. Const int is different from int because int can be modified, that part is correct. The reason his code works is because the `int` is passed by value, so the int and the const int are two separate objects, one created as a copy of the other. – jalf Sep 09 '10 at 23:25
  • 1
    @mathepic: I know what pointers are. ;) – Alerty Sep 10 '10 at 00:10
  • @Alerty Okay, thanks for verifying this. Is it so wrong to have a different view of the question than everyone else? – alternative Sep 10 '10 at 00:26
  • 1
    @jalf: apples vs motorcycles.... lol – Brian R. Bondy Sep 10 '10 at 00:30
  • 1
    @mathepic, nope it is not wrong. Your unique opinion is as important as the one from anyone else. What happened is that a majority of people thought the same way I did about the question. :P – Alerty Sep 10 '10 at 00:48
  • 2
    There is a school of thought saying that everything that can be `const` should be `const`. Since the `const`-ness of a value parameter is an implementation detail, you would then make the parameter non-`const` in the declaration but `const` in the definition if you were to follow this school. – Philipp Sep 10 '10 at 07:13
  • Possible duplicate of [Top-level const doesn't influence a function signature](https://stackoverflow.com/questions/17208570/top-level-const-doesnt-influence-a-function-signature) – underscore_d Apr 18 '18 at 18:46

4 Answers4

35

Because it doesn't matter to the caller of the foo function whether foo modifies its copy of the variable or not.

Specifically in the C++03 standard, the following 2 snippets explain exactly why:

C++03 Section: 13.2-1

Two function declarations of the same name refer to the same function if they are in the same scope and have equivalent parameter declarations (13.1).

C++03 Section: 13.1-3

Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.

Brian R. Bondy
  • 339,232
  • 124
  • 596
  • 636
  • 2
    +1: The second quote is easily misunderstood in the absence of what follows in the Standard "Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.112)". So 'int const *p' and 'int volatile *p' are different – Chubsdad Sep 10 '10 at 01:10
  • @Chubsdad: Agree I should have put more there, added it. Thanks. – Brian R. Bondy Sep 10 '10 at 01:27
  • Thanks for the answer. Quick question: why does my IDE not complain about const char *str in a function declaration, but does about const char ch (the latter, I imagine, is what was explained in the answer)? – Gregor Hartl Watters Apr 04 '22 at 20:27
13

Top-level const (i.e., that applies to the value that's passed, not something to which it points or refers) affects only the implementation, not the interface, of a function. The compiler ignores it from the interface viewpoint (i.e., the calling side) and enforces it only on the implementation (i.e., code in the body of the function).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I quite like to see the standard quoted, so long as the quote makes sense stripped out of its context - it is, after all, the definitive point. –  Sep 10 '10 at 03:24
  • 3
    @Steve314: How *dare* you imply that my saying something is any less definitive than quoting the standard! :-) – Jerry Coffin Sep 10 '10 at 04:08
  • @Steve the accepted answer just quoted non-normative notes from the Standard. Someone who knows the matter (@Jerry) is at least as good as a non-normative note (which are usually formulated slender and sometimes are even wrong), I deeply believe. – Johannes Schaub - litb Sep 12 '10 at 12:03
  • @Johannes - I upvoted Jerry too. I said I like to see the standard quoted - I never claimed to dislike explanation or even completely standard-free answers. In simple terms, there is no one perfect form for an answer - I almost always reject the one true way. But Matteos comment seemed to imply quoting the standard was bad, and I felt it was worth saying otherwise. Perhaps I could have worded it better, but I'm very bad at anticipating misunderstandings. People usually accuse me of being long-winded and pedantic when I try - and misinterpret the tone as arrogant etc. It's a no win thing. –  Sep 15 '10 at 19:22
2

As others have explained, the Standard says it's ok, and that the compiler can afford to be lenient about enforcing this because it doesn't affect the caller, but nobody's answered why the compiler should choose to be lenient. It's not particularly lenient in general, and a programmer who's just been looking at the interface then dives into the implementation may have it in the back of their mind that a parameter is const when it's not or vice versa - not a good thing.

This leniency allows implementation changes without modifying headers, which using traditional make tools triggers recompilation of client code. This can be a serious issue in enterprise scale development, where a non-substantive change in a low-level header (e.g. logging) can force rebuilding of virtually all objects between it and the applications... wasting thousands of hours of CPU time and delaying everyone and everything waiting on the builds.

So, it's ugly, but a practical concession.

I've also answered another similar question which looks at why overloading of f(const T) and f(T) isn't allowed - may be of interest to anyone reading this - Top-level const doesn't influence a function signature

Community
  • 1
  • 1
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • I don't see it as leniancy. I can write a declaration and assignment `const int x = y;` even if y happens to be a non-const variable. It's important that the value of x stays constant in its lifetime - not that the value of y remain likewise constant. I can likewise write `int x = y;` even if y happens to be a constant. The parameter passing is doing much the same thing. It's not leniancy because there's no rule to be leniant about enforcing - the formal parameter is given a value (not a variable) by the call, and there's no reason why the source of that value should be non-modifiable. –  Sep 11 '10 at 05:04
  • @Steve: the leniency aspect relates to accepting a declaration that states you will or will not be able to modify the parameter, then a definition that doesn't match. As you say, either would be fine, but that doesn't mean they're equivalent or consistent. – Tony Delroy Sep 13 '10 at 10:43
  • In my mindset it does. A value simply doesn't have a `const` - it is a property of variables, members, parameters etc, but not of values. Values are always immutable anyway - 5 is always 5 and so on. This simple idea is very good for predicting how C++ will behave whether you're looking at variable initialisations, parameters and overloading, or whatever. In any case, parameters always behave very like local variables - only the initialization method differs, and that consistent with the rule above. For overloading to care about top-level const would seem very inconsistent to me. –  Sep 15 '10 at 19:04
  • @Steve314: reviving the dead here ;-). Your comments are concerned with whether the caller need care if a parameter's const or not... they need not. That's alone makes it possible for - but doesn't explain why - the Standard allows a declaration of f(const T) or f(T) when the definition's f(T) or f(const T) respectively. It doesn't matter to the caller, but these are inconsistent statements about the constness of the parameter within f, and the leniency to compile them need not have been extended. I never suggested that strictly differentiating them be used to enable overloading. – Tony Delroy Jun 21 '13 at 02:54
-3
int foo (const int a)
{
   a++;
   return a;
}

That'll throw an error during compiling.

Esselans
  • 1,540
  • 2
  • 24
  • 44