Functions with const arguments and Overloading - c++

Was tryin out the stackeroverflow qn so it got me thinking why not overload the the function and I came up with a slightly different code but it says the function cannot be overloaded. My question is why? or is there a another way?
#include <iostream>
using std::cout;
class Test {
public:
Test(){ }
int foo (const int) const;
int foo (int );
};
int main ()
{
Test obj;
Test const obj1;
int variable=0;
do{
obj.foo(3); // Call the const function
obj.foo(variable); // Want to make it call the non const function
variable++;
usleep (2000000);
}while(1);
}
int Test::foo(int a)
{
cout<<"NON CONST"<<std::endl;
a++;
return a;
}
int Test::foo (const int a) const
{
cout<<"CONST"<<std::endl;
return a;
}

You can't overload based only on the constness of a non pointer, non reference type.
Think for instance if you were the compiler.
Faced with the line:
cout <<obj.foo(3);
which function would you call?
As you are passing by value the value gets copied either way. The const on the argument is only relevant to the function definition.

§13.1 where the Standard discusses about declarations that cannot be overloaded states -
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 [...]
Only the const and volatile
type-specifiers at the outermost level
of the parameter type specification
are ignored in this fashion; const and
volatile type-specifiers buried within
a parameter type specification are
significant and can be used to
distinguish overloaded function
declarations. [...]
when determining which function is
being declared, defined, or called.
"In particular, for any type T,
“pointer to T,” “pointer to const T,”
and “pointer to volatile T” are
considered distinct parameter types,
as are “reference to T,” “reference to
const T,” and “reference to volatile
T.”
EDIT 2:
As the post is essentially the same as the reffered post, except that the overloaded functions are now class member functions, I am trying to illustrate an additional aspect that could be useful to illustrate the concept of overloading which is not the same as overloading based on the 'constness' of the arguments (either in class scope or namespace scope). However the OP wanted to know how to differentiate the two overloads.
A way to overload them successfully relies on the cv qualification of the implied first parameter in case of member function calls as shown. The 'const' member function can only be called when the object expression used to invoke the overloaded member function is also a const. When a non const object expression is used to invoke the overloaded member function call, the non const version is preferred as it is an exact match (the call to const member function overload will require cv qualification of the first implied argument)
#include <iostream>
using std::cout;
class Test {
public:
Test(){}
int foo (const int) const;
int foo (int );
};
int main ()
{
Test obj;
Test const objc; // const object
obj.foo(3); // calls non const overload, object expression obj is non const
objc.foo(3); // calls const overload, object expression objc is const
}
int Test::foo(int a)
{
a++;
return a;
}
int Test::foo (const int a) const
{
return a;
}

As the answer to the other question explains, the two foos do not differ because they have equivalent parameter definitions.

Related

Is it safe to provide a function pointer with a const argument as a function pointer with a nonconst argument?

I have read in this that in C it must be safe (based on the answers)
And I was wondering if it is safe in C++ to do the same (based on answers in this, it should be). I have attached my code which compiles to show exactly what I am trying to ask.
I have a function that has a function pointer as its argument. The argument is a function pointer with nonconst arguments but I supply that function with a pointer to a function with const arguments.
GCC did not complain about it and I think it should be safe but want to make sure I am not leaving any details.
typedef uint16_t (myFunction)(uint16_t);
uint16_t increment(const uint16_t input);
uint16_t callFunction(myFunction* function, const uint16_t input);
uint16_t increment(const uint16_t input)
{
return input + 1;
}
uint16_t callFunction(myFunction* function, const uint16_t input)
{
return (*function)(input);
}
void setup()
{
callFunction(increment, 2);
/* add setup code here */
}
void loop()
{
/* add main program code here */
}
After reading the comment from Öö Tiib. I decided to make an answer but with some detail so that I can marke the question as answered not wasting other users' time.
Based on the Working Draft, Standard for Programming Language C++ (N4727) in section 16.1 Overloadable declarations
3.4 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) { /* ... */ } // definition of f(int)
int f (cInt) { /* ... */ } // error: redefinition of f(int)
Only the const and volatile type-specifiers at the outermost level of
the parameter type specification are ignored in this fashion; const
and volatile type-specifiers buried within a parameter type
specification are significant and can be used to distinguish
overloaded function declarations. In particular, for any type T,
“pointer to T”, “pointer to const T”, and “pointer to volatile T” are
considered distinct parameter types, as are “reference to T”,
“reference to const T”, and “reference to volatile T”.
Based on the description above it is concluded that there must not be any issue.

C++ const correctness with reference members

I have a fstream & member in a class, on which I'm calling the seekg function in a const function of the class, and yet the code compiles. I checked, and the seekg is not declared const (nor should it be), so how is this happening?
This is my code:
class Test {
fstream &f;
public:
Test(fstream &f_): f(f_) {}
int fid() const {
f.seekg(5);
return 0;
}
};
It turns out the const does not apply to members that are pointers or references, as stated here.
The best explanation I've seen of this is here where it states that inside const member functions, this is a const T *, where T is the class.
In your example that means that all the const modifier on fid() does is to change this from a Test * to a const Test * inside the function. When you write f., this is accessed as this->f. which is of type fstream & const. The reference is const but what it refers to is not, so calling a function that modifies it causes no problems.
The rule is defined in [expr.ref]/4:
If E2 is declared to have type “reference to T”, then E1.E2 is an lvalue; the type of E1.E2 is T. [...]
In practice you should consider a reference to T, as a const pointer to T with automatic dereferencement. Internaly this is what are reference. And inside the standard, all rules that applies to reference (see [basic.life] for example) are those rules that would apply to a const pointer:
class Test {
fstream * const f;
public:
Test(fstream &f_): f(&f_) {}
int fid() const {
f->seekg(5);
return 0;
}
};

Overloading with “const” at end of function declaration

I have 3 attempts to overload function "begin()"
class Test
{
public:
//case 1: compiler error
int begin();
const int begin();
//case 2:compiler warning: type qualifiers ignored on function return type
int begin();
const int begin() const;
//case 3: ok
int begin();
int begin() const;
};
For the two cases that build successfully, how does the compiler choose which overload is called for begin()?
Member functions have an implicit argument which is the instance of the class itself. So you can think your functions as really looking like:
// 1) compile-error as const-qualifications on return doesn't distinguish
// the functions - you cannot overload on return type
int begin(Test& );
const int begin(Test& );
// 2)
int begin(Test& );
const int begin(const Test& );
// 3)
int begin(Test& );
int begin(const Test& );
With the 2nd and 3rd cases, the const-qualification on the function is equivalent to that implicit argument being a reference-to-const. So when you have something like:
Test{}.begin();
That calls begin() using a reference to non-const Test as the implicit first argument. Both overloads of begin() are viable, both require no conversions, so the least cv-qualified reference is preferred, which is the non-const-qualified function.
Instead, when you have:
(const Test{}).begin();
we're calling begin() with a reference to const Test. So the non-const-qualified function is not a viable candidate (you cannot pass a const-ref to a function expecting a non-const-ref), so the best viable candidate is the only viable candidate: int begin() const.
Case 1 is ambiguous cause return type doesn't participate in overload resolution,
case 2 is equivalent to case 3 because const is discarded in return type
In case 3:
const Test t1;
t1.begin() // calls int begin() const;
Test t2;
t2.begin() // calls int begin();
From cppreference.com:
A non-static member function can be declared with a const, volatile,
or const volatile qualifier (this qualifier appears after the name of
the function in function declaration). Differently cv-qualified
functions have different types and so may overload each other. In the
body of a cv-qualified function, the this pointer is cv-qualified,
e.g. in a const member function, only other const member functions may
be called normally. (A non-const member function may still be called
if const_cast is applied or through an access path that does not
involve this.)
A few rules to remember:
You can't overload methods based just on the return type. This leads to the error in your Case 1.
When returning by value, the const qualifier is redundant. In your case 2, compiler is warning you about this.
Every non-static method of the class has an implicit argument. On method invocation, this is assigned to the instance on which the method is invoked. The const keyword at end of method declaration(before the method body) applies to this implicit argument. What you are essentially saying is that the body of the function will not modify the state(data members) of the instance. If you call the method on a const instance, then you need to define the const method. Also, you can overload the method solely based upon the fact whether it is const or not.

Can I pass constant pointers disguised as arrays?

void foo(const char *s);
is equivalent to:
void foo(const char s[]);
Are there similar equivalents to the following two?
void foo(char * const s);
void foo(const char * const s);
In C++, the compiler will automatically convert function parameters of type array of N elements of type T (where N can be unknown) into pointer to T. In the same transformation top level const qualifiers for the arguments are dropped:
void f( const int x ); // => declares: void f( int )
void f( int * const ); // => declares: void f( int* )
That means that in the pointer case, the top level const qualifier is removed, and in the case of the array it is converted to pointer to. Now, on the other hand you cannot mix both, only because you cannot declare a constant array of N elements of type T, since arrays are always const. That means that you cannot write:
void f( const int a[] const ); // Invalid type definition
As the type of the parameter is invalid. If it was a valid type, then the conversions would apply, but because it is not, the compiler will reject the code before trying to perform the conversion.
This is treated in §8.3.5/3 of the C++03 standard (and probably somewhere close in C++11)
A single name can be used for several different functions in a single scope; this is function overloading (clause 13). All declarations for a function with a given parameter list shall agree exactly both in the type of the value returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of the function type. The type of a function is determined using the following rules. The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, several transformations take place upon these types to determine the function type. Any cv-qualifier modifying a parameter type is deleted. [Example: the type void(*)(const int) becomes void(*)(int) —end example] Such cv-qualifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. If a storage-class-specifier modifies a parameter type, the specifier is deleted. [Example: register char* becomes char* —end example] Such storage-class-specifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. The resulting list of transformed parameter types is the function’s parameter type list.
Note that since the compiler will perform that conversion, it is better to write the actual type that is going to be used by the compiler, following the principle of least surprise:
void f( int a[10] ) { a[5] = 7; }
The compiler is not going to check that the passed array has 10 elements, it reads the declaration as void f( int * ), and will gladly accept a call with an array of less elements or even no array at all (a pointer to a single int). Using a pointer in the actual code:
void f( int *a ) { a[5] = 7; }
Will likely trigger some alarms in a code review: are we guaranteed that in all calls to f the argument will be at least 6 elements big? Should we not pass also the size just in case?
You cannot in C89, but in C99 you can declare the equivalents as:
void foo(char s[const]);
void foo(const char s[const]);
this will be useful in some cases:
class AA {
void foo(char a[]);
void foo(const char a[]);
};
void AA::foo(char* const a) { … }
void AA::foo(const char* const a) { … }
and in C:
extern void foo(char a[]);
extern void fooc(const char a[]);
void foo(char* const a) { … }
void fooc(const char* const a) { … }
I thought that a pointer can be null, while an array argument cannot be null (and that the compiler is permitted to optimize knowing that; however on a simple example gcc-4.6 don't do such an optimization, even with -O3).
I am expecting that the compiler would optimize differently the two functions below. It does not. I don't have my C standard at hand to check if it could remove the test in ss below.
int s (int *t)
{
if (!t)
return 0;
return t[0] + t[1];
}
int ss (int t[])
{
if (!t) // never false, since t is an array!!
return 0;
return t[0] + t[1];
}

is there a use for &func or class::func in c++?

This seems inconsistent. Why do we use &Example::func instead of Example::func? is there a use for Example::func or &exampleFunction? it doesnt seem like we can make a reference to a function so that rules out Example::func. and i cant think of a way to use &exampleFunction since exampleFunction already returns a pointer.
#include <iostream>
class Example {
public:
void func() { std::cout <<"print me\n"; }
};
void exampleFunction() { std::cout << "print me too\n"; }
typedef void (Example::*ExampleFunc_t)();
typedef void (*ExampleFunction_t)();
int main()
{
Example e;
ExampleFunc_t f = &Example::func;
ExampleFunction_t f2 = exampleFunction;
(e.*f)();
f2();
return 0;
}
Because that's how the standard defines pointers to functions.
You actually always have to use the address operator & to get a pointer to a function, but for regular functions and static member function, an implicit conversion from function to pointer-to-function is defined in the standard.
This is not defined for a (non static) memberfunction because you can't obtain an lvalue to a non static memberfunction.
From the C++ standard:
4.3 Function-to-pointer conversion
An lvalue of function type T can be converted to an rvalue of type
“pointer to T .” The result is a pointer to the function.
with footnote 52:
This conversion never applies to non-static member functions because
an lvalue that refers to a non-static member function cannot be obtained.
I think that they'd rather only allow &function, for consistency reasons, but that the implicit conversion is simply an artifact of the C heritage...
The point is using Function Pointers. For a very rough example, suppose you have a class having several member variables by which you may need to sort of the elements of an array of that class type, like this:
struct CL {
int x, y, z;
};
bool sort_by_x(const CL& obj1, const CL& obj2);
bool sort_by_y(const CL& obj1, const CL& obj2);
bool sort_by_z(const CL& obj1, const CL& obj2);
...
CL obj[100];
...
sort(obj, obj+100, sort_by_x);
...
sort(obj, obj+100, sort_by_y);
...
sort(obj, obj+100, sort_by_z);
Here std::sort is used to sort an array of CL objects. Look at the third parameter, it's name of a function. The std::sort function can take a function pointer in third parameter, and use that function as comparator to sort the array. It is upto us how to define sort_by_* functions so that std::sort works as expected.