This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Overloading by return type
Why can't I declare three member functions like this:
void x(int a);
void x(String a);
int x(String a);
?
Because you can't overload by return type.
void x(string a)
and
int x(string a)
have the same signature. The signature is made of:
function name
parameters
cv-qualifiers
Which, in your case, are the same.
C++ does not allow you to overload functions based on the return type. Function overloading is only allowed based on the type of the arguments. This means that void x(String a) and int x(String a) are seen as the same function as far as overloading rules are concerned.
One important case that may be confusing (but is often used) is when const is put at the end of a member function. That would look something like int number_of_peanuts(bool tasty_only) const. That const at the end means that the class that this member function is part of cannot be modified by this function.
However, this is actually just a special case of argument type overloading. When you have a member function that is not declared static, there is implicitly an extra parameter added to your function, this this pointer. This means that the example function I gave is roughly equivalent to int number_of_peanuts(Class const * this, bool tasty_only). If you didn't have const at the end of the function, then instead it would be like int number_of_peanuts(Class * this, bool tasty_only).
So to summarize, the type and number of arguments are the only thing that allow you to overload. If you pass by value, as in void x(int a), then const won't give you overload opportunities, because the outside world cannot tell the difference between whether you modify your copy or not. If you pass by reference or pass a pointer, then you can use const with the thing they are referring to as overload options, so void x(std::string & a) and void x(std::string const & a) are different, because the world can tell if you modify a or not. Putting const at the end of the function is another source of overload opportunity. And finally, and most obviously, void x(int a) and void x(int a, int b) is a legal overload because you have a different number of arguments.
Related
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
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.
Consider the following code snippet:
class MyClass {
int x;
public:
MyClass(int val) : x(val) {}
const int& get() const {return x;}
};
void print (const MyClass& arg) {
cout << arg.get() << '\n';
}
int main() {
MyClass foo (10);
print(foo);
return 0;
}
Whether I add a const modifier before the instatiatation of MyClass or not, the program successfully compiles (without any warning) and prints 10. Why can print accept a non-const argument? Or, in other words, what is the function of the const modifier in the function parameter? Why can the formal and actual parameters of a function have different types (or modifiers)?
I have tried both GCC (4.8.2) and Clang (3.4) with -Wall -std=c++11 on Ubuntu 14.04, and the results were the same (no errors/warnings). I have also searched "c++ const object function" but didn't get anything that looked promising.
This is completely sane and normal. The object is treated as const within your function; it does not matter that it was not originally created to be immutable.
Of course, the opposite is not true!
void foo(T& rarr);
int main()
{
const T lolwut;
foo(lolwut); // oops
}
const forbids the body of the function from modifying the parameter variable. Both ways compile because you didn't attempt to modify it.
You can overload const and non-const reference parameters, and the const overload will only be chosen if the argument is really const (or a type conversion results in a temporary being passed). (For non-reference parameters, const seldom makes sense and such overloads may not even be defined.)
All that const does in this case is to prevent modification of parameter variable (and in the case of classes, prevent the calling of functions that are not labelled as const). MyClass may be trivially cast to const MyClass because there should be nothing that you can do to a const MyClass that you can't do to a non-const one. The reverse is not true, of course.
(I say "should" above, because it is of course possible to completely subvert const semantics under C++ if you wanted to, so the presence of const in a function prototype is really only a hopeful hint rather than a cast-iron compiler-enforced guarantee. But no sensible programmer should be breaking things like that!)
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.
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))".