Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning function to function pointer, const argument correctness?

I am learning the basics of C++ and OOP in my university now. I am not 100% sure how a function pointer works when assigning functions to them. I encountered the following code:

void mystery7(int a, const double b) { cout << "mystery7" << endl; }
const int mystery8(int a, double b) { cout << "mystery8" << endl; }

int main() {
    void(*p1)(int, double) = mystery7;            /* No error! */
    void(*p2)(int, const double) = mystery7;
    const int(*p3)(int, double) = mystery8;
    const int(*p4)(const int, double) = mystery8; /* No error! */
}

From my understanding, the p2 and p3 assignments are fine as the function parameters types match and const-ness is correct. But why don't the p1 and p4 assignments fail? Shouldn't it be illegal to match const double/int to non-const double/int?

like image 353
edwardlam0328 Avatar asked Jun 09 '19 14:06

edwardlam0328


People also ask

How do you call a function with a pointer argument?

To pass the value by pointer, argument pointers are passed to the functions just like any other value. So accordingly you need to declare the function parameters as pointer types as in the following function swap(), which exchanges the values of the two integer variables pointed to by its arguments.

Can function pointer passed as an argument?

Pass-by-pointer means to pass a pointer argument in the calling function to the corresponding formal parameter of the called function. The called function can modify the value of the variable to which the pointer argument points. When you use pass-by-pointer, a copy of the pointer is passed to the function.

Why might it be good for a function parameter to be const?

Declaring function parameters const indicates that the function promises not to change these values. In C, function arguments are passed by value rather than by reference. Although a function may change the values passed in, these changed values are discarded once the function returns.


2 Answers

According to the C++ Standard (C++ 17, 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.

So in the process of determining of the function type the qualifier const for example of the second parameter of the function declaration below is discarded.

void mystery7(int a, const double b);

and the function type is void( int, double ).

Also consider the following function declaration

void f( const int * const p );

It is equivalent to the following declaration

void f( const int * p );

It is the second const that makes the parameter constant (that is it declares the pointer itself as a constant object that can not be reassigned inside the function). The first const defines the type of the pointer. It is not discarded.

Pay attention to that though in the C++ Standard there is used the term "const reference" references themselves can not be constant opposite to pointers. That is the following declaration

int & const x = initializer;

is incorrect.

While this declaration

int * const x = initializer;

is correct and declares a constant pointer.

like image 153
Vlad from Moscow Avatar answered Nov 05 '22 14:11

Vlad from Moscow


There is a special rule for function arguments passed by value.

Although const on them will affect their usage inside the function (to prevent accidents), it's basically ignored on the signature. That's because the constness of an object passed by value has no effect whatsoever on the original copied-from object at the call site.

That's what you're seeing.

(Personally I think that this design decision was a mistake; it's confusing and unnecessary! But it is what it is. Note that it comes from the same passage that silently changes void foo(T arg[5]); into void foo(T* arg);, so there's plenty of hokey bullsh!t in there already that we have to deal with!)

Do recall, though, that this doesn't just erase any const in such an argument's type. In int* const the pointer is const, but in int const* (or const int*) the pointer is non-const but is to a const thing. Only the first example relates to constness of the pointer itself and will be stripped.


[dcl.fct]/5 The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) 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 of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. — end note ]

like image 45
Lightness Races in Orbit Avatar answered Nov 05 '22 12:11

Lightness Races in Orbit