const keyword position in function declaration [duplicate] - c++

This question already has answers here:
Closed 10 years ago.
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?
What is the meaning of each of the declarations
Why does all 3 declarations conflict with object functions but only 2 with pointer functions?
Please elaborate that conflict is between l12 and l13, even though l12 does not contain const keyword
Really sorry if trivial question

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.

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).

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

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.

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.

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.

Related

Multiple functions with the same name but their parameters are either constant or received by value or by reference

The title is a bit lengthy, but it's best explained by an example:
Suppose we have the following functions in C++:
void SomeFunction(int num) { //1
}
void SomeFunction(int& num) { //2
}
void SomeFunction(const int& num) { //3
}
void SomeFunction(const int num) { //4
}
All of these are called the same way:
SomeFunction(5);
or
int x = 5;
SomeFunction(x);
When I tried to compile the code, it rightfully says more than one instance of overloaded function "SomeFunction" matches the argument
My question is: Is there a way to tell the compiler which function I meant to call?
I asked my lecturer if it was possible, and she tried something along
SomeFunction< /*some text which I don't remember*/ >(x);
But it didn't work and she asked me to find out and tell her.
I also encounter this post:
How to define two functions with the same name and parameters, if one of them has a reference?
And it seems that 1 and 2 can't be written together, but what about 3 and 4? Can either one of those be called specifically?
1 and 4 have the same signature, so you'll need to drop one of those.
The other functions cannot be called directly, but you could add a template function that allows you to specify the desired parameter type:
template<class Arg>
void Call(void f(Arg), Arg arg)
{
f(arg);
}
// Driver Program to test above functions
int main()
{
int i;
Call<int>(SomeFunction, 1);
Call<int&>(SomeFunction, i);
Call<const int&>(SomeFunction, 1);
}
Alternatively you could use a function pointer to choose the signature.
int i;
static_cast<void(*)(int)>(&SomeFunction)(1);
static_cast<void(*)(int&)>(&SomeFunction)(i);
static_cast<void(*)(const int&)>(&SomeFunction)(1);
It would be preferrable to avoid this scenario though and only define overloads for either references or the signature void SomeFunction(int).
Note:
SomeFunction<some text which I don't remember>(x);
only works for template functions and SomeFunction is not a template function, so this is not an option here.
You can HACK it, and I mean it - it's not a good solution to your problem, by static casting your function explicitly:
static_cast<void(*)(int)>(SomeFunction)(i);
static_cast<void(*)(int&)>(SomeFunction)(i);
static_cast<void(*)(const int&)>(SomeFunction)(i);
Demo
It will work for first 3 overloads. 4th one is equivalent to 1st: quote from the standard [over.load]:
Parameter declarations that differ only in the presence or absence of const and/or volatile are
equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when
determining which function is being declared, defined, or called
and there is an example:
int f (int);
int f (const int); // redeclaration of f(int)
Also note that you cannot call 2nd overload with rvalue (temporary).
The only way I see this working the way your lecturer tried is if SomeFunction is a template and these four overloads are specializations.
template<typename T>
void SomeFunction(T num);
template<>
void SomeFunction<int>(int num) {}
template<>
void SomeFunction<int&>(int& num) {}
template<>
void SomeFunction<const int&>(const int& num) {}
template<>
void SomeFunction<const int>(const int num) {}
Then you can call it as follows.
SomeFunction<int>(x);
SomeFunction<int&>(x);
SomeFunction<const int&>(x);
SomeFunction<const int>(x);
Demo
However, this is incredibly stupid in this context. There are a lot of things wrong with the original overloads.
In the 4th one, the const is completely useless from the caller's perspective, because you can call it the same way you can call the 1st, and the argument is a copy anyway. The const only makes it so the argument is constant inside the function. Moreover, the 1st and 4th overloads cannot both be defined at the same time: the const is actually ignored in the prototype and it leads to a redefinition.
The 3rd overload is also useless because a const int& argument provides no benefit over a int argument. In fact, the compiler probably optimizes that away. The only difference is in the scenario I describe at the end. Of course, if the argument type is more complex (not just int or some other fundamental type), it often makes sense.
The 2nd overload is the only one that can modify the variable you pass as argument. However, if the 1st (or 4th, since it's really the same) overload is present as well, you cannot call the 2nd directly because the call would be ambiguous. You could still call the 1st with an rvalue (basically a literal or an expression like std::move(x)).
If the 2nd and 3rd overloads are the only ones present, then there is no ambiguity and you can call the 2nd with non-const lvalues and the 3rd with const lvalues or rvalues.
Demo

Are some usages of const only really useful when returning or passing things by reference? Or do they have subtle uses I'm not seeing

I've read about the various places to put const. Some usages seem clearly useful to me. Others however evade me. It would be really helpful if someone could confirm or correct my understanding as I explain my mental model of things.
These are the same. I'm not sure I understand why this would ever be useful, though. Does it perhaps allow one to initialize const int variables with a function, which in turn allows some compiler optimizations?
const int foo();
int const foo();
These are the same. The returned pointer cannot be used (via dereferencing) to change the values pointed to.
const int * foo();
int const * foo();
This means the returned pointer itself cannot be changed. But, why would it matter if the caller essentially decides to ignore the returned pointer and set it to something else? Is this only really useful if the pointer is returned by reference?
int * const foo();
These are the same. It means you can only pass in const ints, which allows the compiler to optimize things.
int foo(const int foo);
int foo(int const foo);
This means the passed-in pointer cannot be changed. I'm wondering here too, why would it matter unless the pointer is being passed in by reference?
int foo(int * const foo);
This (as a member function) guarantees that the function won't change the state of the object. Also, if the object itself is declared const, then it will only be able to call such functions.
int foo(int foo) const;
const int as return type is pointless, because in an expression the type of a non-class prvalue result of a function call will be stripped of its top-level const anyway. There is no distinction between a const and non-const prvalue of a non-class type. So the type of foo() is just int, no matter whether it is declared to return const int or int.
It would be different if the return type was a const qualified class type, e.g. const std::string foo(). In that case the const would disallow calling non-const-qualified member functions directly on the result of foo(), e.g. foo().resize(42). Still, this is a very rarely used distinction. And as noted in the comments, under certain conditions it can prevent move operations. E.g. in the above if we have a std::vector<std::string> v;, then v.push_back(foo()) will cause a copy, rather than a move, of the returned string into the vector.
However, the const qualifier is part of the return type in the function type and therefore it is technically possible to differentiate a function declared with const return type from one without it. The type of int foo(int foo) is int(int), but the type of const int foo(int foo) is const int(int). (However overloading based on return type is not possible for non-template functions anyway. The return type is not part of the function signature.)
correct
Same as 1. The type of foo() is simply int*.
The top-level const in the function parameter does not affect the type or signature of the function (in contrast to 1. where it does affect the type). So int foo(const int foo); and int foo(int foo); declare the same function and both have type int(int). Top-level const also doesn't affect how a variable can be initialized, so it doesn't make sense to say "you can only pass in const int". There are no const-qualified prvalues of type int anyway and if int foo can be initialized from some expression, then so can const int foo. The const has no implication on initialization or overload resolution. However const can be used like this in a function definition to tell the compiler and yourself that this parameter is not intended to be modified in the definition.
Same as 4.
This is the correct idea, although in the details it is not strictly true. Rather the const is only relevant to overload resolution (behaving as if the implicit object parameter was a const reference) and the type of this (which will be a pointer to const). It is still possible to mutate members declared as mutable or to use const_cast to mutate members. It is also not relevant whether the object itself is const, only whether the glvalue through which the member function is called is.

Is the `const` keyword used in function declaration, or definition or both?

void test(int& in);
void test(const int& in){
}
int main(){
int a = 5;
test(a);
return 0;
}
Above doesn't compile with a link error: undefined reference to `test(int&)'.
I have 3 questions on this:
1- Why do we get a link error? is it because adding const to the definition makes it a completely different function? why would it work when not using references, i.e this works fine:
void test(int in);
void test(const int in){}
..
int a = 5;
test(a);
..
2- Does const go in function declaration, or definition or both? Seems like
the behaviour is different if references are used.
3- Does the const keyword on an argument say "the parameter passed to me should be a constant in the caller" or "this parameter is treated as a constant in this function scope, regardless of it being constant in the caller or not". I'm sure it's the latter but wanted to confirm.
In C++ the two functions:
void test(int& in);
void test(const int& in);
are TWO different, overloaded functions. The first binds to "writeable" integers, the second - to constant ones.
In your code:
int a = 5;
test(a);
a is a modifiable object, hence void test (int &) is a better match from compiler perspective and it selects this function for a call.
The reason why you are getting linker error is that you declared but not defined this function.
In both cases below the const function would have been selected:
int const a = 5;
test(a);
test(10);
Additionally if you only had const version declared as below, it would have been selected as well:
//void test(int &in);
void test(const int &in){}
..
int a = 5;
test(a);
..
As for the second question in case of references - const goes both to declaration and definition as these are different functions.
With normal values there is NO difference between the two when declared:
void test(int in);
void test(const int in);
They refer to THE SAME function. The const version will prevent modification of the function parameter in function definition.
For the third one, when applied to references it means two things:
A reference will be passed to a function and not a copy for an object.
When accompanied by a const it promises to the caller not to modify referenced object and prevents the function from doing so.
A function-definition is always also a function-declaration.
Thus, to differentiate them, the latter is often called a forward-declaration if it is not also a definition.
The function-signature used for overload-resolution derives from the function-declaration, and there are a few peculiarities in deriving it from the written source:
Top-level cv-specifiers on argument-types are discarded.
Top-level cv-specifiers on return-type are discarded if the type is not a class- or union-type. (A struct is a class-type.)
The function is not affected by these rules outside overload-resolution and matching declarations.
Applied to your examples:
void test(int& in);
void test(const int& in) { // const is not top-level
// The above two declarations are for different functions
void test(int in);
void test(const int in){} // const is top-level
// Equivalent declarations above
A function definition is also a function declaration. Thus, you declare two overloaded functions, but only one function is defined, has a body.
The compiler choses the first function, since a is not const and the first choice is the exact match.
You get the linker error, since the first function has no definition, i.e. no body.
It's best to do both, as the const keyword is intended to hint at the user that the variable will not be modified. I believe most compilers will treat a const type as a different type as well, so you'll have a compile error.

Why are argument modifiers (i.e., 'const' or 'volatile') not considered part of a function's type or signature?

Note that the following two functions have the same type and signature:
void foo1(int t) {} // foo1 has type 'void(*)(int)', and signature '(*)(int)'
void foo2(const int t) {} // Also type 'void(*)(int)', signature '(*)(int)'
(the const is not part of the function type or function signature). Similarly, a modifier (const or volatile) on the return type does not influence the function type or function signature.
However, in the function definition itself (not shown), the named variable t does maintain the const qualification in foo2.
There are many StackOverflow questions discussing why the return type of the function is not considered as part of the function signature (used for overload resolution).
However, I cannot find any StackOverflow question that asks why argument modifiers (const or volatile) are not part of the function's type or signature. Also, I have looked directly in the C++11 standards document and find it difficult to unravel.
What is the rationale behind the fact that argument modifiers (i.e., const and volatile) are not part of a function's type or signature?
ADDENDUM For clarity, from R.MartinhoFernandes's answer below, I should clarify that in C++ (I think) the argument modifiers const and volatile are only ignored as part of the function type/signature if they are top-level modifiers - see that answer below.
What is the rationale behind the fact that argument modifiers (i.e., const and volatile) are not part of a function's type or signature?
From a caller's perspective, there is no difference between void foo(int) and void foo(int const). Whatever you pass to it will not be modified, regardless of the modifier: the function will get a copy.
From an implementer's perspective the sole difference is that with void foo(int x) you can mutate x (i.e. your local copy) in the body, but you cannot mutate x with void foo(int const x).
C++ acknowledges these two perspectives. The caller's perspective is acknowledged by making the two declarations void foo(int); and void foo(int const); declare the same function. The implementer's perspective is acknowledged by allowing you to declare a function as void foo(int x); but define it as void foo(int const x) { /*...*/ } if you want to make sure you don't accidentally assign to the argument.
Note that this only applies for top-level const, i.e. const that applies to the whole type. In things like int const& or int const* the modifier only applies to a part of the type, as "pointer to (const (int))", so it is not top-level const. In int *const however, the const again applies to the whole type as in "const (pointer to (int))".

const parameter

In C++, does the following make sense?
main()
{
int a=10;
fun(a);
}
void fun(const int a)
{
...
}
I can see a program similar to this compile but have linker issues. I just wanted to confirm if assigning a non const var to a const var is apt in C++.
Yes, it is OK.
a cannot be reassigned in fun(), exactly as if it would have been declared that way:
void fun(int param)
{
const int a(param);
...
a = 5; // this is illegal and won't compile.
}
As it is passed by copy, there is no impact on main()'s a anyway. Even if fun()'s a was declared as non-const and modified.
In a function declaration the top level1 const is stripped out by the compiler, while it is kept in the definition.
The reason is that the top level element is copied, and from the point of view of the caller it does not really matter whether the copy is constant or not, so on that end const-ness is not an issue. On the other end, in the definition of the function the argument cont-ness is maintained as it can help the compiler detect unintended modifications of the argument inside the function.
So basically:
void foo( int );
void foo( const int ); // redeclaration of the same function
void foo( const int x ) {
++x; // error: x is const!!
}
In the code above there are two exactly equivalent declarations of foo (the compiler will remove the const from the declaration), and a single definition. Because the in the signature of foo in the definition x is declared as a constant integer, the compiler will complain if you try to increment it.
Some people will use the const in the definition to get the compiler to flag ++x as erroneous, but it is not common. On the other hand, whether the argument is declared as int or const int, for the caller they are the same.
1 Note that this only applies to the top level const, which is applicable to arguments passed by value and pointers, but never to references (a reference is always const: you cannot reassign the reference). So these are different function declarations:
void f( int& );
void f( int const & );
void f( int* ); // equivalent to void f( int * const )
void f( int const * ); // equivalent to void f( int const * const )
It would make sens if you added return types to your functions and had forward-declared fun before you called it (or reversed the order of your functions in that compilation unit).
For a plain int, it doesn't change anything for the caller - the called function can't change the value of that variable in the caller regardless of that qualifier. But for the called function, the parameter is const, so it can't be modified - that makes a difference, not sure if that generally "usefull" or not.
Now consider this:
int foo(int& a);
int bar(int const& a);
Those are two different beasts. bar can only read a, and can take either a plain int or a const int (or a const int&).
foo, on the other hand, can change a if it sees fit, and cannot take a const.
See the Const correctness entry in the C++FAQ Lite for more on this, including information about how this applies (or not) to const-pointers, pointers-to-const and const-pointer-to-const.
It doesn't make much sense because what you get in the function is a copy of the variable, not the variable itself. But this is legitimate.
The "assignment" is ok - a in fun is a new variable which is kept constant inside fun.
But fun should have a return type, i.e. void.
And there should be a declaration of fun before using it in main.
You can assign a non-const to a const