Why is following not allowed in C++
#include <iostream>
class Sample {
public:
void Method(char x);
void Method(char const x);
};
void Sample::Method(char x) {
char y = x;
}
void Sample::Method(char const x) {
char y = x;
}
int main() {
Sample s;
return 0;
}
Why is following not allowed in C++?
The reason is the very same that the compiler gives you as an compilation error:
Because they are ambiguous!
Why are these methods ambiguous?
Short answer: Because the C++ Standard says so.
What is the rationale behind these overloaded methods being ambiguous?
The compiler does not know whether the caller wants to treat the value of the passed argument as an const or not, there is no way for the compiler to determine that with the information at hand.
Note the emphasis on pass by value here, the argument is being passed by value, and hence the ambiguity. If the argument was passed by reference then the compiler knows for sure how the caller wants to treat the argument because then the actual object itself is being passed, and hence compiler can make a selection of the proper overload.
The following example gives a clearer idea to the explanation above.
Online Sample:
class Sample
{
public:
void Method(char &x){}
void Method(char const x){}
void Method(char const &x){}
};
int main()
{
Sample s;
return 0;
}
It doesn't really answer why, but it is determined by the standard, ยง1.3.10
The information about a function that participates in overload resolution (13.3): the types of its parameters
and, if the function is a class member, the cv- qualifiers (if any) on the function itself and the class in which the member function is declared.
This just means the cv qualifiers of the arguments are ignored in the overload resolution.
A similar (but not equivalent) example with references works:
class Sample {
public:
void Method(char& x) {}
void Method(const char& x) {}
};
because here the types are different, the first case being a reference to char, the second a reference to const char (as opposed to a const reference to char).
When it comes to function parameters, char and char const are the same data type.
This is still ambiguous. When it's called with a character argument, one version will copy the argument and say "OK, you can change the copy". The other will copy the argument and say "OK, you cannot change the copy." How is the compiler supposed to know whether it can or can't change a copy of something? It could do either just fine.
because it's ambiguous
when you're passing like this
s.Method('x');
what version should you think be called?
The standard says those two declarations are equivalent (13.1.3):
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.
typedef const int cInt;
int f(int);
int f(const int); // redeclaration of f(int)
int f(int) { /* ... */ } // definiton of f(int)
int f(cInt) { /* ... */ } // error: redefiniton of f(int)
http://duramecho.com/ComputerInformation/WhyHowCppConst.html
Because const denotes that variable as having a set value, that cannot be changed after declaration. It is not a different data type.
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.
As I have seen from C++ Primer,fourth edition,
"What may be surprising, is that although the parameter is a const inside the function, the
compiler otherwise treats the definition of fcn as if we had defined the parameter as a plain int:"
void fcn(const int i) { /* fcn can read but not write to i */ }
void fcn(int i) { /* ... */ } // error: redefines fcn(int)
So, if I want to define two functions as follows,
int func(const int i) {
return i;
}
int func(int i){
i++;
return i;
}
Actually,they are two different functions,but when I compile it,there is a message:error:redefinition of 'int func(int i)'.Can I define them like this? Is there any alternative methods?
Parameter passed by value (like your int) are copied onto the stack,
then if your function takes a 'const int' as an argument, it does not change what can be done from outside the function (it justs forbids you from changing the value inside the function). Since the parameter is copied, a function that takes an int as an argument can be called on a const int (it will not modify the argument since it gets a copy).
Since there is no difference 'seen from outside' of the function between the one that takes an int and a const int, there would be no reason for the compiler to choose one version or the other, this is why the C++ norm states that it is the same function.
Now if you have a function that takes a reference f(int &) and another one that takes a const reference f(const int&), it is another story: then only the second one can be called with a const int argument like in:
const int x=5;
f(x);
const just modifies how you access your variable it doesn't change its data type, as far as the compiler is concerned both of them are integers and that's how they are stored whether modifiable or not.
The only workaround i see is to overload the function by changing the data type of your parameter, maybe long, double, byte etc..
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
GCC treats these two function declarations as equivalent:
void F(int* a) { }
void F(int* const a) { }
test.cpp: In function 'void F(int*)':
test.cpp:235: error: redefinition of 'void F(int*)'
test.cpp:234: error: 'void F(int*)' previously defined here
This makes some sense because a caller will always ignore the const in this case... it only affects the usage of the parameter 'a' inside of the function.
What I'm wondering is where (if anywhere) the standard says that it's specifically OK to discard qualifiers on pointers used as function arguments for the purpose of overload resolution.
(My real issue is that I'd like to figure out where GCC strips these pointless qualifiers internally, and since the C++ frontend of GCC is littered with comments referencing the standard, the relevant section of the standard might help me find the correct spot.)
Standard says in 8.3.5/3 that for the purposes of determining the function type any cv-qualifiers that directly qualify the parameter type are deleted. I.e. it literally says that a function declared as
void foo(int *const a);
has function type void (int *).
A pedantic person might argue that this is not conclusive enough to claim that the above declaration should match the definition like this one
void foo(int *a)
{
}
or that it should make the code with dual declaration (as in your example) ill-formed, since neither of these concepts are described in the standard in terms of function types.
I mean, we all know that these const were intended to be ignored for all external purposes, but so far I was unable to find the wording in the standard that would conclusively state exactly that. Maybe I missed something.
Actually, in 13.1/3 it has a "Note" that says that function declarations with equivalent parameter declarations (as defined in 8.3.5) declare the same function. But it is just a note, it is non-normative, which suggests that somewhere in the standard there should be some normative text on the same issue.
I think it is basically as prohibited as this:
void foo(int a) {}
void foo(const int a) {}
const on non-references doesn't participate in overloading.
In fact you could even declare
void foo(int a);
and later define
void foo(const int a) {}
where the constness is purely an implementation detail which the caller doesn't care about.
It's the same as:
void foo(int);
void foo(const int);
Being the same to the caller. This is because the function is getting a copy by-value no matter what, so the caller doesn't care if it's thought of as const or not; it makes no difference to it.
It's not legal for the compiler to ignore such things, but there is no difference in overload resolution. The const applies to the implementation of the function.
Illegal would be if the compiler treated:
void foo(int i)
{
i = 5; // good
}
void foo(const int)
{
i = 5; // lolwut?
}
The same, by ignoring the const.
I believe it's the other way around. Any pointer, even nonconst, can be treated like const :).