On 31.05.2020 18:13, Juha Nieminen wrote:
> It's still unclear to me what the difference is between the function type
> syntax of the form "ReturnType(*)(ParameterTypes)" and
> "ReturnType(ParameterTypes)".
Ben Bacarisse has already answered this, and some of the following, but
I'll try to add a higher level perspective.
As he noted the * notation declares a pointer to function, while the
notation without that, declares a function.
Functions and function pointers are often interchangeable, just as
arrays and array item pointers are often interchangeable, because
* they have implicit conversion to pointer where a pointer is needed,
called a type DECAY, and
* the main functionality is only defined for pointers.
This is understandable given the history of C designed as a kind of
portable assembly language to ease the porting of Unix. In assembly
language everything is done via single values and pointers. With that
perspective it's the pointers that are primary, and the function and
array types are only sort of helpers to type check the pointer usage
(and for arrays, to allocate storage for automatic variables).
For example, there is no such thing as indexing of an array: formally
indexing is defined for pointers only.
And for example, there is no such thing as a call of a function: the
call syntax is available for pointers only.
Indexing and calling works for actual arrays and functions only because
of the implicit conversions to pointer.
> Sometimes they seem to be interchangeable, sometimes they don't.
>
> For example, this is valid:
>
> //-------------------------------------------
> using F1 = int(*)(int);
> using F2 = int(int);
Yes, but you can't declare a variable of type F2. If you try to then the
compiler understands that as a function declaration, not a variable.
Because functions are not data in C++. Or from a higher level
perspective, C++ is designed to support Harvard architecture machines
where code lives in a separate address space. So there is no such thing
as a variable of function type.
However, you can use F2 as a parameter type. Just as with an array type
it then decays. The type that F2 as parameter type expresses, is a pointer.
> void foo1(F1 funcPtr); // ok
> void foo2(F2 funcPtr); // ok
Yes, these mean exactly the same, due to the decay of the F2 type.
> //-------------------------------------------
>
> However, this is not valid:
>
> //-------------------------------------------
> F1 funcPtr1 = someFunc; // ok
> F2 funcPtr2 = someFunc; // error
> //-------------------------------------------
Yep. Here `funcPtr2` is not a pointer, it's a function. And one cannot
initialize a function.
> Likewise:
>
> //-------------------------------------------
> std::function<int(int)> funcObj1; // ok
> std::function<int(*)(int)> funcObj2; // error
>
> std::set<int, bool(*)(int, int)> set1; // ok
> std::set<int, bool(int, int)> set2; // error
> //-------------------------------------------
>
> So they are like the reverse of each other.
>
> F2 above can be used for a function declaration, which might give some
> insight into what it means. In other words:
>
> //-------------------------------------------
> F2 someFunction; // ok
>
> int main()
> {
> int value = someFunction(5); // ok.
> }
>
> int someFunction(int v) { return v+1; }
> //-------------------------------------------
Note that this use of a function type F2 adds a third quirk: that in a
declaration of a non-static member function the type changes to, not
pointer, but a member function type distinct from F2.
Example:
-------------------------------------------------------------------------
#include <iostream>
using namespace std;
using F = void(int);
struct Gah
{
int data;
F func;
};
void Gah::func( int x ) { cout << x << endl; }
auto main()
-> int
{
Gah().func( 666 );
#ifdef SURPRISE
Gah g = {};
int* pd = &g.data; // OK.
F* pf = &g.func; //! No, not same type.
(void) pd; (void) pf;
#endif
}
-------------------------------------------------------------------------
Visual C++ 2019 reasonably just protests about the syntax. After all
it's invalid syntax.
g++ 9.2 unreasonably accepts the syntax, but then reacts to the obvious
meaning and says
error: cannot convert 'void (Gah::*)(int)' to 'void (*)(int)' in
initialization
- Alf