I recently found out that you can add and remove cv-qualifier from value arguments in a function declaration and it's implementation. For instance:
F.h
f(const int i);
F.cpp
f(int i) { i++; }
test.cpp
#include "F.h"
f(0);
The f(int i) implementation will be called. Since its a copy by value, I see no problems with the code compiling. But can anyone imagine a case, where it's somehow useful to add the const to the declaration as done above? Why is it possible at all? Maybe someone can give a more useful application?
Top-level CV qualifiers on function arguments are ignored, i.e. they are not part of the function signature. Rather, if you will, they are part of the implementation of the function, since they qualify the local variables corresponding to the formal function parameters. Therefore, if it pleases you, you may qualify function parameters in the function definition:
int power(int val, int n, int base);
int power(int val, int n, const int base)
{
while (n --> 0) val *= base;
return val;
}
Some people find it offensive if the definition has different-looking function parameters from the declaration, though. It's a matter of taste and style. The benefits of qualifying the arguments are probably small, though there is some value for the implementation in documenting that you don't plan to change a variable.
Some people like to state in their definition that they are not going to change a parameter even though it is only visible locally. For example:
void f(int);
void f(int const);
void f(int const x) {
x = 3; // ERROR!
}
The definition is the function which was just declared, i.e., there is no change on the signature (well, unless you happen to use Sun's CC which actually takes the const into account for the mangled name but that's just an error). All three signatures declare exactly the same function and the const only affects the body of the definition if it is used in the definition's declaration.
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.
Given a function in C++ with arguments that are only types and have no identifiers,
void foo1(int, int, int){cout << "called foo1";}
I can call it as such:
int main()
{
foo1(10, 10, 10);
}
Why is this a valid construct in C++? Is this just an idiosyncrasy of C++, or does this kind of declaration actually have some purpose? Can we actually access the arguments that have been passed in somehow?
(This kind of method declaration will not work in Java.)
Consider a case where you are required to provide a function that meets the following prototype
void dostuff(int x, int y, int z);
And say you are operating in a 2D space and do not use z inside your implementation. You can
void dostuff(int x, int y, int z)
{
// use x and y
}
and just ignore z, but the compiler will likely spot that you have defined but not used z and warn you that you could be making a mistake. Instead you can
void dostuff(int x, int y, int )
{
// use x and y
}
and leave out the definition of z. The compiler will accept and silently discard the third parameter because it knows you don't want it.
You do not want to simply turn off the warning because of errors like this
void dostuff(int x, int y, int z)
{
for (int z = 0; z < MAX; z++)
{
// use x and y and z, the local z.
}
}
Where a poorly-named loop index shadows the parameter z. The caller's input is now ignored and this could have bad consequences. This error is often hard to spot with the mark 1 eyeball, especially if the local z is buried somewhere deep in a complex function.
Anytime the compiler can pick off a possible bug in your code, take advantage. It means less work for you.
A declaration like
void foo1(int, int, int){cout << "called foo1";}
clearly shows in the declaration, that you want to fulfill a requirement with your function - e.g. override a specific function in the base class or interface, which e.g. could be declared there as
virtual void foo1(int something, int another, int andAnother) = 0;
BUT you don't intend to use the parameters which are handed over to you.
Another example would be if you want to hand over the function to e.g. another function which expects a function pointer to a void function with three int parameters.
void giveMeFoo( void (*fn)(int, int, int) ) { ... }
Additionally, higher warning levels issue a warning, if parameters are declared, which are not evaluated in the function body. You can avoid that by leaving the parameter names away.
The parameters without names are then indeed not accessible in the function body anymore - on purpose. user4581301 has nicely described, why.
Declaring a standalone function without parameter names as in your example is allowed because of the usages described above, but it obviously makes no sense in most cases. An example where it does make sense is in the comment section. Another example of a standalone function without parameter names could be, if your'e writing a library and want to either maintain backward compatibility (your library function does not need the parameter anymore, but you don't want to break the public header declaration) or you want to reserve a parameter for future use.
Yes. It is legal in C++.
C++11 n3337 standard 8.4.1(p6) Function definitions:
Note: Unused parameters need not be named. For example,
void print(int a, int) {
std::printf("a = %d\n", a);
}
C++14 standard:
[ 8.3.5.11] An identifier can optionally be provided as a parameter name; if present in a function definition , it names a parameter
(sometimes called “formal argument”). [Note: In particular, parameter
names are also optional in function definitions and names used for a
parameter in different declarations and the definition of a function
need not be the same.]
It's legal, and if you're wondering why:
Typically, unnamed arguments arise from the simplification of code or
from planning ahead for extensions. In both cases, leaving the
argument in place, although unused, ensures that callers are not
affected by the change.
Excerpt From: Bjarne Stroustrup. “The C++ Programming Language, Fourth Edition.”
The idea is that you might want to change the function
definition to use the placeholder later, without changing all the
code where the function is called.
Arguments in a function declaration can be declared without
identifiers. When these are used with default arguments, it can look
a bit funny. You can end up with :
void f(int x, int = 0, float = 1.1);
In C++ you don’t need identifiers in the function definition, either:
void f(int x, int, float flt) { /* ... */ }
In the function body, x and flt can be referenced, but not the
middle argument, because it has no name. Function calls must still
provide a value for the placeholder, though: f(1) or f(1,2,3.0). This
syntax allows you to put the argument in as a placeholder without
using it.
Unnamed parameters can also be used for the tag dispatch idiom:
template<typename T>
void foo(T&& t){
foo(std::forward<T>(t), std::is_same_v<std::remove_ref<std::remove_const<T>>>, std::string>); // do something special with strings
template<typename T>
void foo(T&& t, std::false_type); // note no parameter name
template<typename T>
void foo(T&& t, std::true_type);
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.
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 :).