9

Possible Duplicate:
Difference between const declarations in C++

#include <iostream>

class Bar{};

void foo(const Bar x){}  //l5
void foo(Bar x){}        //l6
void foo(Bar const x){}  //l7

////pointer functions

void foo(const Bar* x){} //l11
void foo(Bar* x){}       //l12
void foo(Bar* const x){} //l13

Compiler output: (long story short l5,l6,l7 conflict; but only l12,l13 conflict)

untitled.cpp:6:6: error: redefinition of ‘void foo(Bar)’
untitled.cpp:5:6: error: ‘void foo(Bar)’ previously defined here
untitled.cpp:7:6: error: redefinition of ‘void foo(Bar)’
untitled.cpp:5:6: error: ‘void foo(Bar)’ previously defined here
untitled.cpp:13:6: error: redefinition of ‘void foo(Bar*)’
untitled.cpp:12:6: error: ‘void foo(Bar*)’ previously defined here

What going on?

  1. What is the meaning of each of the declarations
  2. Why does all 3 declarations conflict with object functions but only 2 with pointer functions?
  3. Please elaborate that conflict is between l12 and l13, even though l12 does not contain const keyword
  4. Really sorry if trivial question
Community
  • 1
  • 1
aiao
  • 4,621
  • 3
  • 25
  • 47

6 Answers6

6

The "problem" is that constness of a parameter's value doesn't participate in overloading!

First, Bar const and const Bar are already identical meaning, so they would automatically have a problem. But as a function parameter the const doesn't apply to overloading so the Bar version of the function also looks the same too. The const in the paremeter only tells the compiler that you don't intend to modify it in the function body.

For the same reason, Bar* and Bar* const are treated the same: The const applies to the value of the parameter (not what's pointed to) and does not participate in overloading, so you've defined the same function.

On the other hand const Bar* means something totally different: A non-const pointer to a const object (of type Bar). Since the type is different it does participate in overloading and allows that function to be unique.

Mark B
  • 95,107
  • 10
  • 109
  • 188
2

It doesn't matter whether you put const before or after the type name.

15 and 17 have the same parameter argument list.
These 2 functions are considered to have the same prototype and are duplicates.

Function #1

void foo(const int x) {
return;
}

Function #2 - Duplicate parameter argument list

void foo( int const x) {
return;
}

The position of const is the same as 15 and 17 in the example you have.

Either will work according to Wikipedia:

http://en.wikipedia.org/wiki/Const-correctness

A B
  • 4,068
  • 1
  • 20
  • 23
  • Re your first sentence: it doesn't matter to the compiler, in this particular context. Generally, however, `const` modifies what precedes it, and putting the `const` systematically after what it modifies (i.e. `int const`, and not `const int`) does make the code more readable. – James Kanze Jan 09 '13 at 19:28
2

Basically, because C++ copies values when calling a function, there is nothing to distinguish the first three from a callers perspective. (The caller knows the function can't change it's own value it's passing in, so every function parameter is implicitly constant in a lot of regards, from the perspective of the caller at any rate).

When you talk about pointers however, if you pass a pointer to a constant vs a pointer to a non constant, there is a difference to the caller (one wont change your stuff, the other might). This is why l11 and l12 don't conflict.

l12 and l13 conflict though because they are both pointers to Bar* (one is a const pointer, one is not, so same problem as l5-l7, there's no difference to the caller).

This last point may be a little tricky - note that while int const *a is the same as const int *a, these are not the same as int * const a, the first two are pointers to a constant int, the other is a constant pointer to an int (ie the value of the pointer can't change in the later).

hexist
  • 5,151
  • 26
  • 33
0

The reason the first three create a conflict, is that the compiler can't figure out which function to use in any case. When you call foo(BarObject); the compiler could very well use any of them whether was declared BarObject as const or not.

However on the ones with parameters as pointers, when you call foo(BarPointer); if BarPointer was declared as const Bar* BarPointer; the compiler will pick ]11, because it ensures the object pointed to will not be modified in the function (not the case when passing by value as in the first three). If it isn't const, it doesn't know if it should call ]12 or ]13 because what Bar* const x means is, "x can't point to anything else than what was passed as a parameter" and this doesn't concern the caller.

Small reference to the declarations:

const Bar x // x is an immutable copy of the original parameter.
Bar const x // same as above and gets compiled, but cdecl says it is a syntax error.

const Bar* x // x points to an object that can't be changed.
Bar* const x // x can't point to any other object than the parameter passed.
imreal
  • 10,178
  • 2
  • 32
  • 48
0
void foo(const Bar x){}
void foo(Bar const x){}

The above two are identical because both say x is of Bar type and it is const.

void foo(Bar x){}

This one conflicts with the above 2 because whether x is const or not is an implementation detail of your function, and is discarded by the compiler from the function signature. So all 3 functions end up having the same signature which is void foo( Bar x ).

void foo(Bar* x){}
void foo(Bar* const x){}

This is similar to the previous case; you're indicating that the pointer x is const i.e. you will not re-point x to something thing else within your function. In both cases the Bar object that x points to is non-const. Thus the constness of x is an implementation detail of the function.

void foo(const Bar* x){}

Here you're indicating that x points to a Bar object which is const. This is different from the previous 2 cases, and so there is no conflict.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
0

For the first three function - const doesn't matters on overloading resolution in case variable transferred by value. Copy of argument created on the stack and it doesn't make sense if this copy changed or not from the outside(caller) point of view. It matters for function itself (inside).

For second case, pointer based functions, it's important part of function overload resolution, because copies are not created on the stack and from the outside(caller) point of view it means function will or not modify argument's value.

For last two functions use say to a compiler: there is x pointer to Bar, and this Bar value pointed by x I may change. But in first case you can change value of pointer x itself (say, point to another Bar) opposite to the second case. And here we are in the first situation - copies of pointers themselves are on the stack and it doesn't make sense for overloading resolution if they changed inside of the function or not.

demi
  • 5,384
  • 6
  • 37
  • 57